Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
a7bb692a
Unverified
Commit
a7bb692a
authored
Feb 15, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Feb 15, 2024
Browse files
Merge pull request #751 from ollama-webui/styling
feat: fullscreen mode
parents
7e2508e4
dc322084
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
256 additions
and
219 deletions
+256
-219
src/lib/components/chat/MessageInput.svelte
src/lib/components/chat/MessageInput.svelte
+15
-8
src/lib/components/chat/MessageInput/Documents.svelte
src/lib/components/chat/MessageInput/Documents.svelte
+1
-1
src/lib/components/chat/MessageInput/Models.svelte
src/lib/components/chat/MessageInput/Models.svelte
+1
-1
src/lib/components/chat/MessageInput/PromptCommands.svelte
src/lib/components/chat/MessageInput/PromptCommands.svelte
+1
-1
src/lib/components/chat/Messages.svelte
src/lib/components/chat/Messages.svelte
+99
-89
src/lib/components/chat/Messages/Placeholder.svelte
src/lib/components/chat/Messages/Placeholder.svelte
+1
-1
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+3
-5
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+2
-2
src/lib/components/layout/Navbar.svelte
src/lib/components/layout/Navbar.svelte
+1
-1
src/lib/components/layout/Sidebar.svelte
src/lib/components/layout/Sidebar.svelte
+6
-2
src/routes/(app)/+page.svelte
src/routes/(app)/+page.svelte
+55
-46
src/routes/(app)/c/[id]/+page.svelte
src/routes/(app)/c/[id]/+page.svelte
+69
-61
src/routes/error/+page.svelte
src/routes/error/+page.svelte
+2
-1
No files found.
src/lib/components/chat/MessageInput.svelte
View file @
a7bb692a
...
@@ -55,6 +55,11 @@
...
@@ -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 @@
...
@@ -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' }
);
scrollTo
Bottom(
);
}}
}}
>
>
<svg
<svg
...
@@ -401,7 +406,7 @@
...
@@ -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 @@
...
@@ -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}
...
...
src/lib/components/chat/MessageInput/Documents.svelte
View file @
a7bb692a
...
@@ -88,7 +88,7 @@
...
@@ -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>
...
...
src/lib/components/chat/MessageInput/Models.svelte
View file @
a7bb692a
...
@@ -120,7 +120,7 @@
...
@@ -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>
...
...
src/lib/components/chat/MessageInput/PromptCommands.svelte
View file @
a7bb692a
...
@@ -47,7 +47,7 @@
...
@@ -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>
...
...
src/lib/components/chat/Messages.svelte
View file @
a7bb692a
...
@@ -29,10 +29,15 @@
...
@@ -29,10 +29,15 @@
$: if (autoScroll && bottomPadding) {
$: if (autoScroll && bottomPadding) {
(async () => {
(async () => {
await tick();
await tick();
window.
scrollTo
({ top: document.body.scrollHeight, behavior: 'smooth' }
);
scrollTo
Bottom(
);
})();
})();
}
}
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 @@
...
@@ -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' }
);
scrollTo
Bottom(
);
}, 100);
}, 100);
};
};
...
@@ -208,9 +214,11 @@
...
@@ -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' }
);
scrollTo
Bottom(
);
}, 100);
}, 100);
};
};
</script>
</script>
...
@@ -218,95 +226,97 @@
...
@@ -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 !== ''}
<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
{#if messages.length - 1 === messageIdx && processing !== ''}
<div class=" dark:text-blue-100">
<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
<svg
<div class=" dark:text-blue-100">
class=" w-4 h-4 translate-y-[0.5px]"
<svg
fill="currentColor"
class=" w-4 h-4 translate-y-[0.5px]"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><style>
xmlns="http://www.w3.org/2000/svg"
.spinner_qM83 {
><style>
animation: spinner_8HQG 1.05s infinite;
.spinner_qM83 {
}
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);
</div>
transform: translateY(-6px);
<div class=" text-sm font-medium">
}
{processing}
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>
{/if}
{:else}
<ResponseMessage
{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}
src/lib/components/chat/Messages/Placeholder.svelte
View file @
a7bb692a
...
@@ -16,7 +16,7 @@
...
@@ -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}
...
...
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
a7bb692a
...
@@ -270,9 +270,7 @@
...
@@ -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 @@
...
@@ -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 @@
...
@@ -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>
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
a7bb692a
...
@@ -3,7 +3,7 @@
...
@@ -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 @@
...
@@ -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'
});
});
...
...
src/lib/components/layout/Navbar.svelte
View file @
a7bb692a
...
@@ -69,7 +69,7 @@
...
@@ -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
...
...
src/lib/components/layout/Sidebar.svelte
View file @
a7bb692a
...
@@ -89,10 +89,14 @@
...
@@ -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"
...
...
src/routes/(app)/+page.svelte
View file @
a7bb692a
...
@@ -137,6 +137,11 @@
...
@@ -137,6 +137,11 @@
});
});
};
};
const scrollToBottom = () => {
const element = document.getElementById('messages-container');
element.scrollTop = element.scrollHeight;
};
//////////////////////////
//////////////////////////
// Ollama functions
// Ollama functions
//////////////////////////
//////////////////////////
...
@@ -316,7 +321,7 @@
...
@@ -316,7 +321,7 @@
await tick();
await tick();
// Scroll down
// Scroll down
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
const messagesBody = [
const messagesBody = [
$settings.system
$settings.system
...
@@ -440,7 +445,7 @@
...
@@ -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 @@
...
@@ -469,7 +474,7 @@
}
}
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
}
}
...
@@ -508,7 +513,7 @@
...
@@ -508,7 +513,7 @@
await tick();
await tick();
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
if (messages.length == 2 && messages.at(1).content !== '') {
if (messages.length == 2 && messages.at(1).content !== '') {
...
@@ -519,8 +524,7 @@
...
@@ -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 @@
...
@@ -628,7 +632,7 @@
}
}
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
}
}
...
@@ -672,7 +676,7 @@
...
@@ -672,7 +676,7 @@
await tick();
await tick();
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
if (messages.length == 2) {
if (messages.length == 2) {
...
@@ -783,47 +787,52 @@
...
@@ -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>
src/routes/(app)/c/[id]/+page.svelte
View file @
a7bb692a
...
@@ -153,6 +153,11 @@
...
@@ -153,6 +153,11 @@
}
}
};
};
const scrollToBottom = () => {
const element = document.getElementById('messages-container');
element.scrollTop = element.scrollHeight;
};
//////////////////////////
//////////////////////////
// Ollama functions
// Ollama functions
//////////////////////////
//////////////////////////
...
@@ -330,7 +335,7 @@
...
@@ -330,7 +335,7 @@
await tick();
await tick();
// Scroll down
// Scroll down
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
const messagesBody = [
const messagesBody = [
$settings.system
$settings.system
...
@@ -454,7 +459,7 @@
...
@@ -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 @@
...
@@ -483,7 +488,7 @@
}
}
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
}
}
...
@@ -522,7 +527,7 @@
...
@@ -522,7 +527,7 @@
await tick();
await tick();
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
if (messages.length == 2 && messages.at(1).content !== '') {
if (messages.length == 2 && messages.at(1).content !== '') {
...
@@ -534,7 +539,7 @@
...
@@ -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 }
);
scrollTo
Bottom(
);
const res = await generateOpenAIChatCompletion(localStorage.token, {
const res = await generateOpenAIChatCompletion(localStorage.token, {
model: model,
model: model,
...
@@ -642,7 +647,7 @@
...
@@ -642,7 +647,7 @@
}
}
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
}
}
...
@@ -686,7 +691,7 @@
...
@@ -686,7 +691,7 @@
await tick();
await tick();
if (autoScroll) {
if (autoScroll) {
window.
scrollTo
({ top: document.body.scrollHeight }
);
scrollTo
Bottom(
);
}
}
if (messages.length == 2) {
if (messages.length == 2) {
...
@@ -797,66 +802,69 @@
...
@@ -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="flex flex-col flex-auto">
<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
<div
bind:selectedModels
class="{$settings?.fullScreenMode ?? null
disabled={messages.length > 0 && !selectedModels.includes('')}
? '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 ??
{processing}
$config.default_prompt_suggestions}
bind:history
{messages}
bind:messages
{submitPrompt}
bind:autoScroll
{stopResponse}
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>
{/if}
{/if}
src/routes/error/+page.svelte
View file @
a7bb692a
<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 @@
...
@@ -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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment