Merge branch 'dev' into feature/support_auth_by_api_key
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
export let isLastMessage = true;
|
||||
|
||||
export let readOnly = false;
|
||||
|
||||
export let confirmEditResponseMessage: Function;
|
||||
export let showPreviousMessage: Function;
|
||||
export let showNextMessage: Function;
|
||||
@@ -128,7 +130,7 @@
|
||||
// • auto-render specific keys, e.g.:
|
||||
delimiters: [
|
||||
{ left: '$$', right: '$$', display: false },
|
||||
{ left: '$', right: '$', display: false },
|
||||
{ left: '$ ', right: ' $', display: false },
|
||||
{ left: '\\(', right: '\\)', display: false },
|
||||
{ left: '\\[', right: '\\]', display: false },
|
||||
{ left: '[ ', right: ' ]', display: false }
|
||||
@@ -422,7 +424,7 @@
|
||||
class=" flex justify-start space-x-1 overflow-x-auto buttons text-gray-700 dark:text-gray-500"
|
||||
>
|
||||
{#if siblings.length > 1}
|
||||
<div class="flex self-center min-w-fit -mt-1">
|
||||
<div class="flex self-center min-w-fit">
|
||||
<button
|
||||
class="self-center dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
@@ -203,7 +204,7 @@
|
||||
|
||||
<div class=" flex justify-start space-x-1 text-gray-700 dark:text-gray-500">
|
||||
{#if siblings.length > 1}
|
||||
<div class="flex self-center -mt-1">
|
||||
<div class="flex self-center">
|
||||
<button
|
||||
class="self-center dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
@@ -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"
|
||||
|
||||
@@ -258,6 +258,9 @@
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const error = await fileResponse?.json();
|
||||
toast.error(error?.detail ?? error);
|
||||
}
|
||||
|
||||
if (uploaded) {
|
||||
|
||||
@@ -1,41 +1,180 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import { getContext, onMount } from 'svelte';
|
||||
|
||||
import fileSaver from 'file-saver';
|
||||
const { saveAs } = fileSaver;
|
||||
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
|
||||
import { chatId, modelfiles } from '$lib/stores';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import Link from '../icons/Link.svelte';
|
||||
|
||||
let chat = null;
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let downloadChat: Function;
|
||||
export let shareChat: Function;
|
||||
const shareLocalChat = async () => {
|
||||
const _chat = chat;
|
||||
|
||||
const sharedChat = await shareChatById(localStorage.token, $chatId);
|
||||
const chatShareUrl = `${window.location.origin}/s/${sharedChat.id}`;
|
||||
|
||||
toast.success($i18n.t('Copied shared chat URL to clipboard!'));
|
||||
copyToClipboard(chatShareUrl);
|
||||
chat = await getChatById(localStorage.token, $chatId);
|
||||
};
|
||||
|
||||
const shareChat = async () => {
|
||||
const _chat = chat.chat;
|
||||
console.log('share', _chat);
|
||||
|
||||
toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
|
||||
const url = 'https://openwebui.com';
|
||||
// const url = 'http://localhost:5173';
|
||||
|
||||
const tab = await window.open(`${url}/chats/upload`, '_blank');
|
||||
window.addEventListener(
|
||||
'message',
|
||||
(event) => {
|
||||
if (event.origin !== url) return;
|
||||
if (event.data === 'loaded') {
|
||||
tab.postMessage(
|
||||
JSON.stringify({
|
||||
chat: _chat,
|
||||
modelfiles: $modelfiles.filter((modelfile) =>
|
||||
_chat.models.includes(modelfile.tagName)
|
||||
)
|
||||
}),
|
||||
'*'
|
||||
);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
const downloadChat = async () => {
|
||||
const _chat = chat.chat;
|
||||
console.log('download', chat);
|
||||
|
||||
const chatText = _chat.messages.reduce((a, message, i, arr) => {
|
||||
return `${a}### ${message.role.toUpperCase()}\n${message.content}\n\n`;
|
||||
}, '');
|
||||
|
||||
let blob = new Blob([chatText], {
|
||||
type: 'text/plain'
|
||||
});
|
||||
|
||||
saveAs(blob, `chat-${_chat.title}.txt`);
|
||||
};
|
||||
|
||||
export let show = false;
|
||||
|
||||
onMount(async () => {
|
||||
chatId.subscribe(async (value) => {
|
||||
chat = await getChatById(localStorage.token, value);
|
||||
console.log(chat);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal bind:show size="xs">
|
||||
<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
|
||||
<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"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
shareChat();
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Share to OpenWebUI Community')}
|
||||
</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>
|
||||
|
||||
<Modal bind:show size="sm">
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Share Chat')}</div>
|
||||
<button
|
||||
class=" self-center rounded-full text-xs font-medium text-gray-700 dark:text-gray-500 underline"
|
||||
type="button"
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
downloadChat();
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Download as a File')}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<hr class=" dark:border-gray-800" />
|
||||
|
||||
{#if chat}
|
||||
<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
|
||||
<div class=" text-sm dark:text-gray-300 mb-1">
|
||||
{#if chat.share_id}
|
||||
<a href="/s/{chat.share_id}" target="_blank"
|
||||
>You have shared this chat <span class=" underline">before</span>.</a
|
||||
>
|
||||
Click here to
|
||||
<button
|
||||
class="underline"
|
||||
on:click={async () => {
|
||||
const res = await deleteSharedChatById(localStorage.token, $chatId);
|
||||
|
||||
if (res) {
|
||||
chat = await getChatById(localStorage.token, $chatId);
|
||||
}
|
||||
}}>delete this link</button
|
||||
> and create a new shared link.
|
||||
{:else}
|
||||
Messages you send after creating your link won't be shared. Users with the URL will be
|
||||
able to view the shared chat.
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<div class="flex flex-col items-end space-x-1 mt-1.5">
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
shareChat();
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Share to OpenWebUI Community')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class=" self-center flex items-center gap-1 px-3.5 py-2 rounded-xl text-sm font-medium bg-emerald-600 hover:bg-emerald-500 text-white"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
shareLocalChat();
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<Link />
|
||||
|
||||
{#if chat.share_id}
|
||||
{$i18n.t('Update and Copy Link')}
|
||||
{:else}
|
||||
{$i18n.t('Copy Link')}
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex gap-1 mt-1.5">
|
||||
<div class=" self-center text-gray-400 text-xs font-medium">{$i18n.t('or')}</div>
|
||||
<button
|
||||
class=" text-right rounded-full text-xs font-medium text-gray-700 dark:text-gray-500 underline"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
downloadChat();
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Download as a File')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user