forked from open-webui/open-webui
Merge pull request #751 from ollama-webui/styling
feat: fullscreen mode
This commit is contained in:
commit
a7bb692a54
13 changed files with 259 additions and 222 deletions
|
@ -55,6 +55,11 @@
|
||||||
let isRecording = false;
|
let isRecording = false;
|
||||||
const MIN_DECIBELS = -45;
|
const MIN_DECIBELS = -45;
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
const element = document.getElementById('messages-container');
|
||||||
|
element.scrollTop = element.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
const startRecording = async () => {
|
const startRecording = async () => {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
mediaRecorder = new MediaRecorder(stream);
|
mediaRecorder = new MediaRecorder(stream);
|
||||||
|
@ -371,17 +376,17 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="fixed bottom-0 w-full">
|
<div class="w-full">
|
||||||
<div class="px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
<div class="px-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
||||||
<div class="flex flex-col max-w-3xl w-full">
|
<div class="flex flex-col max-w-3xl w-full">
|
||||||
<div>
|
<div class="relative">
|
||||||
{#if autoScroll === false && messages.length > 0}
|
{#if autoScroll === false && messages.length > 0}
|
||||||
<div class=" flex justify-center mb-4">
|
<div class=" absolute -top-12 left-0 right-0 flex justify-center">
|
||||||
<button
|
<button
|
||||||
class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
|
class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
autoScroll = true;
|
autoScroll = true;
|
||||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
scrollToBottom();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -401,7 +406,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full">
|
<div class="w-full relative">
|
||||||
{#if prompt.charAt(0) === '/'}
|
{#if prompt.charAt(0) === '/'}
|
||||||
<Prompts bind:this={promptsElement} bind:prompt />
|
<Prompts bind:this={promptsElement} bind:prompt />
|
||||||
{:else if prompt.charAt(0) === '#'}
|
{:else if prompt.charAt(0) === '#'}
|
||||||
|
@ -432,14 +437,16 @@
|
||||||
bind:chatInputPlaceholder
|
bind:chatInputPlaceholder
|
||||||
{messages}
|
{messages}
|
||||||
/>
|
/>
|
||||||
{:else if messages.length == 0 && suggestionPrompts.length !== 0}
|
{/if}
|
||||||
|
|
||||||
|
{#if messages.length == 0 && suggestionPrompts.length !== 0}
|
||||||
<Suggestions {suggestionPrompts} {submitPrompt} />
|
<Suggestions {suggestionPrompts} {submitPrompt} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white dark:bg-gray-900">
|
<div class="bg-white dark:bg-gray-900">
|
||||||
<div class="max-w-3xl px-2.5 -mb-0.5 mx-auto inset-x-0">
|
<div class="max-w-3xl px-2.5 mx-auto inset-x-0">
|
||||||
<div class=" pb-2">
|
<div class=" pb-2">
|
||||||
<input
|
<input
|
||||||
bind:this={filesInputElement}
|
bind:this={filesInputElement}
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
|
{#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
|
||||||
<div class="md:px-2 mb-3 text-left w-full">
|
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
|
||||||
<div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
|
<div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
|
||||||
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||||
<div class=" text-lg font-semibold mt-2">#</div>
|
<div class=" text-lg font-semibold mt-2">#</div>
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if filteredModels.length > 0}
|
{#if filteredModels.length > 0}
|
||||||
<div class="md:px-2 mb-3 text-left w-full">
|
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
|
||||||
<div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
|
<div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
|
||||||
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||||
<div class=" text-lg font-semibold mt-2">@</div>
|
<div class=" text-lg font-semibold mt-2">@</div>
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if filteredPromptCommands.length > 0}
|
{#if filteredPromptCommands.length > 0}
|
||||||
<div class="md:px-2 mb-3 text-left w-full">
|
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
|
||||||
<div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
|
<div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
|
||||||
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||||
<div class=" text-lg font-semibold mt-2">/</div>
|
<div class=" text-lg font-semibold mt-2">/</div>
|
||||||
|
|
|
@ -29,10 +29,15 @@
|
||||||
$: if (autoScroll && bottomPadding) {
|
$: if (autoScroll && bottomPadding) {
|
||||||
(async () => {
|
(async () => {
|
||||||
await tick();
|
await tick();
|
||||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
scrollToBottom();
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
const element = document.getElementById('messages-container');
|
||||||
|
element.scrollTop = element.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
const copyToClipboard = (text) => {
|
const copyToClipboard = (text) => {
|
||||||
if (!navigator.clipboard) {
|
if (!navigator.clipboard) {
|
||||||
var textArea = document.createElement('textarea');
|
var textArea = document.createElement('textarea');
|
||||||
|
@ -160,10 +165,11 @@
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
const element = document.getElementById('messages-container');
|
||||||
|
autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
scrollToBottom();
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,9 +214,11 @@
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
const element = document.getElementById('messages-container');
|
||||||
|
autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
scrollToBottom();
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -218,95 +226,97 @@
|
||||||
{#if messages.length == 0}
|
{#if messages.length == 0}
|
||||||
<Placeholder models={selectedModels} modelfiles={selectedModelfiles} />
|
<Placeholder models={selectedModels} modelfiles={selectedModelfiles} />
|
||||||
{:else}
|
{:else}
|
||||||
{#key chatId}
|
<div class=" pb-10">
|
||||||
{#each messages as message, messageIdx}
|
{#key chatId}
|
||||||
<div class=" w-full">
|
{#each messages as message, messageIdx}
|
||||||
<div
|
<div class=" w-full">
|
||||||
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
|
<div
|
||||||
? 'max-w-full'
|
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
|
||||||
: 'max-w-3xl'} mx-auto rounded-lg group"
|
? 'max-w-full'
|
||||||
>
|
: 'max-w-3xl'} mx-auto rounded-lg group"
|
||||||
{#if message.role === 'user'}
|
>
|
||||||
<UserMessage
|
{#if message.role === 'user'}
|
||||||
user={$user}
|
<UserMessage
|
||||||
{message}
|
user={$user}
|
||||||
siblings={message.parentId !== null
|
{message}
|
||||||
? history.messages[message.parentId]?.childrenIds ?? []
|
siblings={message.parentId !== null
|
||||||
: Object.values(history.messages)
|
? history.messages[message.parentId]?.childrenIds ?? []
|
||||||
.filter((message) => message.parentId === null)
|
: Object.values(history.messages)
|
||||||
.map((message) => message.id) ?? []}
|
.filter((message) => message.parentId === null)
|
||||||
{confirmEditMessage}
|
.map((message) => message.id) ?? []}
|
||||||
{showPreviousMessage}
|
{confirmEditMessage}
|
||||||
{showNextMessage}
|
{showPreviousMessage}
|
||||||
{copyToClipboard}
|
{showNextMessage}
|
||||||
/>
|
{copyToClipboard}
|
||||||
|
/>
|
||||||
|
|
||||||
{#if messages.length - 1 === messageIdx && processing !== ''}
|
{#if messages.length - 1 === messageIdx && processing !== ''}
|
||||||
<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
|
<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
|
||||||
<div class=" dark:text-blue-100">
|
<div class=" dark:text-blue-100">
|
||||||
<svg
|
<svg
|
||||||
class=" w-4 h-4 translate-y-[0.5px]"
|
class=" w-4 h-4 translate-y-[0.5px]"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
><style>
|
><style>
|
||||||
.spinner_qM83 {
|
.spinner_qM83 {
|
||||||
animation: spinner_8HQG 1.05s infinite;
|
animation: spinner_8HQG 1.05s infinite;
|
||||||
}
|
|
||||||
.spinner_oXPr {
|
|
||||||
animation-delay: 0.1s;
|
|
||||||
}
|
|
||||||
.spinner_ZTLf {
|
|
||||||
animation-delay: 0.2s;
|
|
||||||
}
|
|
||||||
@keyframes spinner_8HQG {
|
|
||||||
0%,
|
|
||||||
57.14% {
|
|
||||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
|
||||||
transform: translate(0);
|
|
||||||
}
|
}
|
||||||
28.57% {
|
.spinner_oXPr {
|
||||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
animation-delay: 0.1s;
|
||||||
transform: translateY(-6px);
|
|
||||||
}
|
}
|
||||||
100% {
|
.spinner_ZTLf {
|
||||||
transform: translate(0);
|
animation-delay: 0.2s;
|
||||||
}
|
}
|
||||||
}
|
@keyframes spinner_8HQG {
|
||||||
</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
|
0%,
|
||||||
class="spinner_qM83 spinner_oXPr"
|
57.14% {
|
||||||
cx="12"
|
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||||
cy="12"
|
transform: translate(0);
|
||||||
r="2.5"
|
}
|
||||||
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg
|
28.57% {
|
||||||
>
|
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||||
|
transform: translateY(-6px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
|
||||||
|
class="spinner_qM83 spinner_oXPr"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="2.5"
|
||||||
|
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class=" text-sm font-medium">
|
||||||
|
{processing}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class=" text-sm font-medium">
|
{/if}
|
||||||
{processing}
|
{:else}
|
||||||
</div>
|
<ResponseMessage
|
||||||
</div>
|
{message}
|
||||||
|
modelfiles={selectedModelfiles}
|
||||||
|
siblings={history.messages[message.parentId]?.childrenIds ?? []}
|
||||||
|
isLastMessage={messageIdx + 1 === messages.length}
|
||||||
|
{confirmEditResponseMessage}
|
||||||
|
{showPreviousMessage}
|
||||||
|
{showNextMessage}
|
||||||
|
{rateMessage}
|
||||||
|
{copyToClipboard}
|
||||||
|
{continueGeneration}
|
||||||
|
{regenerateResponse}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
</div>
|
||||||
<ResponseMessage
|
|
||||||
{message}
|
|
||||||
modelfiles={selectedModelfiles}
|
|
||||||
siblings={history.messages[message.parentId]?.childrenIds ?? []}
|
|
||||||
isLastMessage={messageIdx + 1 === messages.length}
|
|
||||||
{confirmEditResponseMessage}
|
|
||||||
{showPreviousMessage}
|
|
||||||
{showNextMessage}
|
|
||||||
{rateMessage}
|
|
||||||
{copyToClipboard}
|
|
||||||
{continueGeneration}
|
|
||||||
{regenerateResponse}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{/each}
|
||||||
{/each}
|
|
||||||
|
|
||||||
{#if bottomPadding}
|
{#if bottomPadding}
|
||||||
<div class=" mb-10" />
|
<div class=" mb-10" />
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if models.length > 0}
|
{#if models.length > 0}
|
||||||
<div class="m-auto text-center max-w-md pb-56 px-2">
|
<div class="m-auto text-center max-w-md px-2">
|
||||||
<div class="flex justify-center mt-8">
|
<div class="flex justify-center mt-8">
|
||||||
<div class="flex -space-x-4 mb-1">
|
<div class="flex -space-x-4 mb-1">
|
||||||
{#each models as model, modelIdx}
|
{#each models as model, modelIdx}
|
||||||
|
|
|
@ -270,9 +270,7 @@
|
||||||
{#if message.model in modelfiles}
|
{#if message.model in modelfiles}
|
||||||
{modelfiles[message.model]?.title}
|
{modelfiles[message.model]?.title}
|
||||||
{:else}
|
{:else}
|
||||||
Ollama <span class=" text-gray-500 text-sm font-medium"
|
{message.model ? ` ${message.model}` : ''}
|
||||||
>{message.model ? ` ${message.model}` : ''}</span
|
|
||||||
>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if message.timestamp}
|
{#if message.timestamp}
|
||||||
|
@ -365,7 +363,7 @@
|
||||||
{#if message.done}
|
{#if message.done}
|
||||||
<div class=" flex justify-start space-x-1 -mt-2 overflow-x-auto buttons">
|
<div class=" flex justify-start space-x-1 -mt-2 overflow-x-auto buttons">
|
||||||
{#if siblings.length > 1}
|
{#if siblings.length > 1}
|
||||||
<div class="flex self-center">
|
<div class="flex self-center min-w-fit">
|
||||||
<button
|
<button
|
||||||
class="self-center"
|
class="self-center"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
|
@ -386,7 +384,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="text-xs font-bold self-center">
|
<div class="text-xs font-bold self-center min-w-fit">
|
||||||
{siblings.indexOf(message.id) + 1} / {siblings.length}
|
{siblings.indexOf(message.id) + 1} / {siblings.length}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
|
|
||||||
import { createModel, deleteModel, pullModel } from '$lib/apis/ollama';
|
import { createModel, deleteModel, pullModel } from '$lib/apis/ollama';
|
||||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
import { WEBUI_API_BASE_URL, WEBUI_NAME } from '$lib/constants';
|
||||||
import { models, user } from '$lib/stores';
|
import { models, user } from '$lib/stores';
|
||||||
import { splitStream } from '$lib/utils';
|
import { splitStream } from '$lib/utils';
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
} else {
|
} else {
|
||||||
toast.success(`Model '${modelName}' has been successfully downloaded.`);
|
toast.success(`Model '${modelName}' has been successfully downloaded.`);
|
||||||
|
|
||||||
const notification = new Notification(`Ollama`, {
|
const notification = new Notification(WEBUI_NAME, {
|
||||||
body: `Model '${modelName}' has been successfully downloaded.`,
|
body: `Model '${modelName}' has been successfully downloaded.`,
|
||||||
icon: '/favicon.png'
|
icon: '/favicon.png'
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} />
|
<ShareChatModal bind:show={showShareChatModal} {downloadChat} {shareChat} />
|
||||||
<nav
|
<nav
|
||||||
id="nav"
|
id="nav"
|
||||||
class=" fixed py-2.5 top-0 flex flex-row justify-center bg-white/95 dark:bg-gray-900/90 dark:text-gray-200 backdrop-blur-xl w-screen z-30"
|
class=" sticky py-2.5 top-0 flex flex-row justify-center bg-white/95 dark:bg-gray-900/90 dark:text-gray-200 backdrop-blur-xl z-30"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class=" flex {$settings?.fullScreenMode ?? null
|
class=" flex {$settings?.fullScreenMode ?? null
|
||||||
|
|
|
@ -89,10 +89,14 @@
|
||||||
bind:this={navElement}
|
bind:this={navElement}
|
||||||
class="h-screen {show
|
class="h-screen {show
|
||||||
? ''
|
? ''
|
||||||
: '-translate-x-[260px]'} w-[260px] fixed top-0 left-0 z-40 transition bg-black text-gray-200 shadow-2xl text-sm
|
: '-translate-x-[260px] w-[0px]'} w-[260px] min-w[260px] bg-black text-gray-200 shadow-2xl text-sm transition z-40 fixed top-0 left-0 lg:relative
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="py-2.5 my-auto flex flex-col justify-between h-screen">
|
<div
|
||||||
|
class="py-2.5 my-auto flex flex-col justify-between h-screen w-[260px] {show
|
||||||
|
? ''
|
||||||
|
: 'invisible'}"
|
||||||
|
>
|
||||||
<div class="px-2.5 flex justify-center space-x-2">
|
<div class="px-2.5 flex justify-center space-x-2">
|
||||||
<button
|
<button
|
||||||
id="sidebar-new-chat-button"
|
id="sidebar-new-chat-button"
|
||||||
|
|
|
@ -137,6 +137,11 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
const element = document.getElementById('messages-container');
|
||||||
|
element.scrollTop = element.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// Ollama functions
|
// Ollama functions
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -316,7 +321,7 @@
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
// Scroll down
|
// Scroll down
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
|
|
||||||
const messagesBody = [
|
const messagesBody = [
|
||||||
$settings.system
|
$settings.system
|
||||||
|
@ -440,7 +445,7 @@
|
||||||
selectedModelfile.title.charAt(0).toUpperCase() +
|
selectedModelfile.title.charAt(0).toUpperCase() +
|
||||||
selectedModelfile.title.slice(1)
|
selectedModelfile.title.slice(1)
|
||||||
}`
|
}`
|
||||||
: `Ollama - ${model}`,
|
: `${model}`,
|
||||||
{
|
{
|
||||||
body: responseMessage.content,
|
body: responseMessage.content,
|
||||||
icon: selectedModelfile?.imageUrl ?? '/favicon.png'
|
icon: selectedModelfile?.imageUrl ?? '/favicon.png'
|
||||||
|
@ -469,7 +474,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,7 +513,7 @@
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.length == 2 && messages.at(1).content !== '') {
|
if (messages.length == 2 && messages.at(1).content !== '') {
|
||||||
|
@ -519,8 +524,7 @@
|
||||||
|
|
||||||
const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => {
|
const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => {
|
||||||
const responseMessage = history.messages[responseMessageId];
|
const responseMessage = history.messages[responseMessageId];
|
||||||
|
scrollToBottom();
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
|
||||||
|
|
||||||
const res = await generateOpenAIChatCompletion(localStorage.token, {
|
const res = await generateOpenAIChatCompletion(localStorage.token, {
|
||||||
model: model,
|
model: model,
|
||||||
|
@ -628,7 +632,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +676,7 @@
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.length == 2) {
|
if (messages.length == 2) {
|
||||||
|
@ -783,47 +787,52 @@
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window
|
<div class="min-h-screen max-h-screen w-full flex flex-col">
|
||||||
on:scroll={(e) => {
|
<Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} />
|
||||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
<div class="flex flex-col flex-auto">
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} />
|
|
||||||
<div class="min-h-screen w-full flex justify-center">
|
|
||||||
<div class=" py-2.5 flex flex-col justify-between w-full">
|
|
||||||
<div
|
<div
|
||||||
class="{$settings?.fullScreenMode ?? null
|
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
|
||||||
? 'max-w-full'
|
id="messages-container"
|
||||||
: 'max-w-2xl md:px-0'} mx-auto w-full px-4 mt-10"
|
on:scroll={(e) => {
|
||||||
|
autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ModelSelector bind:selectedModels disabled={messages.length > 0} />
|
<div
|
||||||
|
class="{$settings?.fullScreenMode ?? null
|
||||||
|
? 'max-w-full'
|
||||||
|
: 'max-w-2xl md:px-0'} mx-auto w-full px-4"
|
||||||
|
>
|
||||||
|
<ModelSelector
|
||||||
|
bind:selectedModels
|
||||||
|
disabled={messages.length > 0 && !selectedModels.includes('')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" h-full w-full flex flex-col py-8">
|
||||||
|
<Messages
|
||||||
|
chatId={$chatId}
|
||||||
|
{selectedModels}
|
||||||
|
{selectedModelfiles}
|
||||||
|
{processing}
|
||||||
|
bind:history
|
||||||
|
bind:messages
|
||||||
|
bind:autoScroll
|
||||||
|
bottomPadding={files.length > 0}
|
||||||
|
{sendPrompt}
|
||||||
|
{continueGeneration}
|
||||||
|
{regenerateResponse}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" h-full mt-10 mb-32 w-full flex flex-col">
|
<MessageInput
|
||||||
<Messages
|
bind:files
|
||||||
chatId={$chatId}
|
bind:prompt
|
||||||
{selectedModels}
|
bind:autoScroll
|
||||||
{selectedModelfiles}
|
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
||||||
{processing}
|
{messages}
|
||||||
bind:history
|
{submitPrompt}
|
||||||
bind:messages
|
{stopResponse}
|
||||||
bind:autoScroll
|
/>
|
||||||
bottomPadding={files.length > 0}
|
|
||||||
{sendPrompt}
|
|
||||||
{continueGeneration}
|
|
||||||
{regenerateResponse}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MessageInput
|
|
||||||
bind:files
|
|
||||||
bind:prompt
|
|
||||||
bind:autoScroll
|
|
||||||
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
|
||||||
{messages}
|
|
||||||
{submitPrompt}
|
|
||||||
{stopResponse}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -153,6 +153,11 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
const element = document.getElementById('messages-container');
|
||||||
|
element.scrollTop = element.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// Ollama functions
|
// Ollama functions
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -330,7 +335,7 @@
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
// Scroll down
|
// Scroll down
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
|
|
||||||
const messagesBody = [
|
const messagesBody = [
|
||||||
$settings.system
|
$settings.system
|
||||||
|
@ -454,7 +459,7 @@
|
||||||
selectedModelfile.title.charAt(0).toUpperCase() +
|
selectedModelfile.title.charAt(0).toUpperCase() +
|
||||||
selectedModelfile.title.slice(1)
|
selectedModelfile.title.slice(1)
|
||||||
}`
|
}`
|
||||||
: `Ollama - ${model}`,
|
: `${model}`,
|
||||||
{
|
{
|
||||||
body: responseMessage.content,
|
body: responseMessage.content,
|
||||||
icon: selectedModelfile?.imageUrl ?? '/favicon.png'
|
icon: selectedModelfile?.imageUrl ?? '/favicon.png'
|
||||||
|
@ -483,7 +488,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,7 +527,7 @@
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.length == 2 && messages.at(1).content !== '') {
|
if (messages.length == 2 && messages.at(1).content !== '') {
|
||||||
|
@ -534,7 +539,7 @@
|
||||||
const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => {
|
const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => {
|
||||||
const responseMessage = history.messages[responseMessageId];
|
const responseMessage = history.messages[responseMessageId];
|
||||||
|
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
|
|
||||||
const res = await generateOpenAIChatCompletion(localStorage.token, {
|
const res = await generateOpenAIChatCompletion(localStorage.token, {
|
||||||
model: model,
|
model: model,
|
||||||
|
@ -642,7 +647,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,7 +691,7 @@
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
window.scrollTo({ top: document.body.scrollHeight });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.length == 2) {
|
if (messages.length == 2) {
|
||||||
|
@ -797,66 +802,69 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window
|
|
||||||
on:scroll={(e) => {
|
|
||||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#if loaded}
|
{#if loaded}
|
||||||
<Navbar
|
<div class="min-h-screen max-h-screen w-full flex flex-col">
|
||||||
{title}
|
<Navbar
|
||||||
shareEnabled={messages.length > 0}
|
{title}
|
||||||
initNewChat={async () => {
|
shareEnabled={messages.length > 0}
|
||||||
if (currentRequestId !== null) {
|
initNewChat={async () => {
|
||||||
await cancelChatCompletion(localStorage.token, currentRequestId);
|
if (currentRequestId !== null) {
|
||||||
currentRequestId = null;
|
await cancelChatCompletion(localStorage.token, currentRequestId);
|
||||||
}
|
currentRequestId = null;
|
||||||
|
}
|
||||||
|
|
||||||
goto('/');
|
goto('/');
|
||||||
}}
|
}}
|
||||||
{tags}
|
{tags}
|
||||||
{addTag}
|
{addTag}
|
||||||
{deleteTag}
|
{deleteTag}
|
||||||
/>
|
|
||||||
<div class="min-h-screen w-full flex justify-center">
|
|
||||||
<div class=" py-2.5 flex flex-col justify-between w-full">
|
|
||||||
<div
|
|
||||||
class="{$settings?.fullScreenMode ?? null
|
|
||||||
? 'max-w-full'
|
|
||||||
: 'max-w-2xl md:px-0'} mx-auto w-full px-4 mt-10"
|
|
||||||
>
|
|
||||||
<ModelSelector
|
|
||||||
bind:selectedModels
|
|
||||||
disabled={messages.length > 0 && !selectedModels.includes('')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class=" h-full mt-10 mb-32 w-full flex flex-col">
|
|
||||||
<Messages
|
|
||||||
chatId={$chatId}
|
|
||||||
{selectedModels}
|
|
||||||
{selectedModelfiles}
|
|
||||||
{processing}
|
|
||||||
bind:history
|
|
||||||
bind:messages
|
|
||||||
bind:autoScroll
|
|
||||||
bottomPadding={files.length > 0}
|
|
||||||
{sendPrompt}
|
|
||||||
{continueGeneration}
|
|
||||||
{regenerateResponse}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<MessageInput
|
|
||||||
bind:files
|
|
||||||
bind:prompt
|
|
||||||
bind:autoScroll
|
|
||||||
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
|
|
||||||
{messages}
|
|
||||||
{submitPrompt}
|
|
||||||
{stopResponse}
|
|
||||||
/>
|
/>
|
||||||
|
<div class="flex flex-col flex-auto">
|
||||||
|
<div
|
||||||
|
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
|
||||||
|
id="messages-container"
|
||||||
|
on:scroll={(e) => {
|
||||||
|
autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="{$settings?.fullScreenMode ?? null
|
||||||
|
? 'max-w-full'
|
||||||
|
: 'max-w-2xl md:px-0'} mx-auto w-full px-4"
|
||||||
|
>
|
||||||
|
<ModelSelector
|
||||||
|
bind:selectedModels
|
||||||
|
disabled={messages.length > 0 && !selectedModels.includes('')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" h-full w-full flex flex-col py-8">
|
||||||
|
<Messages
|
||||||
|
chatId={$chatId}
|
||||||
|
{selectedModels}
|
||||||
|
{selectedModelfiles}
|
||||||
|
{processing}
|
||||||
|
bind:history
|
||||||
|
bind:messages
|
||||||
|
bind:autoScroll
|
||||||
|
bottomPadding={files.length > 0}
|
||||||
|
{sendPrompt}
|
||||||
|
{continueGeneration}
|
||||||
|
{regenerateResponse}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MessageInput
|
||||||
|
bind:files
|
||||||
|
bind:prompt
|
||||||
|
bind:autoScroll
|
||||||
|
suggestionPrompts={selectedModelfile?.suggestionPrompts ??
|
||||||
|
$config.default_prompt_suggestions}
|
||||||
|
{messages}
|
||||||
|
{submitPrompt}
|
||||||
|
{stopResponse}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import { WEBUI_NAME } from '$lib/constants';
|
||||||
import { config } from '$lib/stores';
|
import { config } from '$lib/stores';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@
|
||||||
<div class="absolute rounded-xl w-full h-full backdrop-blur flex justify-center">
|
<div class="absolute rounded-xl w-full h-full backdrop-blur flex justify-center">
|
||||||
<div class="m-auto pb-44 flex flex-col justify-center">
|
<div class="m-auto pb-44 flex flex-col justify-center">
|
||||||
<div class="max-w-md">
|
<div class="max-w-md">
|
||||||
<div class="text-center text-2xl font-medium z-50">Ollama WebUI Backend Required</div>
|
<div class="text-center text-2xl font-medium z-50">{WEBUI_NAME} Backend Required</div>
|
||||||
|
|
||||||
<div class=" mt-4 text-center text-sm w-full">
|
<div class=" mt-4 text-center text-sm w-full">
|
||||||
Oops! You're using an unsupported method (frontend only). Please serve the WebUI from
|
Oops! You're using an unsupported method (frontend only). Please serve the WebUI from
|
||||||
|
|
Loading…
Reference in a new issue