forked from open-webui/open-webui
refac: styling
This commit is contained in:
parent
db08ad964c
commit
e99d69bfe2
8 changed files with 163 additions and 132 deletions
|
@ -55,6 +55,11 @@
|
|||
let isRecording = false;
|
||||
const MIN_DECIBELS = -45;
|
||||
|
||||
const scrollToBottom = () => {
|
||||
const element = document.getElementById('messages-container');
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
const startRecording = async () => {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
mediaRecorder = new MediaRecorder(stream);
|
||||
|
@ -371,17 +376,17 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="w-full pt-2 md:pt-0">
|
||||
<div class="px-2.5 pt-2.5 -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
|
||||
<div class="w-full">
|
||||
<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>
|
||||
<div class="relative">
|
||||
{#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
|
||||
class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
|
||||
on:click={() => {
|
||||
autoScroll = true;
|
||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
||||
scrollToBottom();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
|
@ -401,7 +406,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="w-full">
|
||||
<div class="w-full relative">
|
||||
{#if prompt.charAt(0) === '/'}
|
||||
<Prompts bind:this={promptsElement} bind:prompt />
|
||||
{:else if prompt.charAt(0) === '#'}
|
||||
|
@ -432,14 +437,16 @@
|
|||
bind:chatInputPlaceholder
|
||||
{messages}
|
||||
/>
|
||||
{:else if messages.length == 0 && suggestionPrompts.length !== 0}
|
||||
{/if}
|
||||
|
||||
{#if messages.length == 0 && suggestionPrompts.length !== 0}
|
||||
<Suggestions {suggestionPrompts} {submitPrompt} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<input
|
||||
bind:this={filesInputElement}
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
</script>
|
||||
|
||||
{#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=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||
<div class=" text-lg font-semibold mt-2">#</div>
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
</script>
|
||||
|
||||
{#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=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||
<div class=" text-lg font-semibold mt-2">@</div>
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
</script>
|
||||
|
||||
{#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=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||
<div class=" text-lg font-semibold mt-2">/</div>
|
||||
|
|
|
@ -29,10 +29,16 @@
|
|||
$: if (autoScroll && bottomPadding) {
|
||||
(async () => {
|
||||
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) => {
|
||||
if (!navigator.clipboard) {
|
||||
var textArea = document.createElement('textarea');
|
||||
|
@ -160,10 +166,11 @@
|
|||
|
||||
await tick();
|
||||
|
||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
||||
const element = document.getElementById('messages-container');
|
||||
autoScroll = element.scrollHeight - element.scrollTop === element.clientHeight - 40;
|
||||
|
||||
setTimeout(() => {
|
||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
@ -208,9 +215,11 @@
|
|||
|
||||
await tick();
|
||||
|
||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
||||
const element = document.getElementById('messages-container');
|
||||
autoScroll = element.scrollHeight - element.scrollTop === element.clientHeight - 40;
|
||||
|
||||
setTimeout(() => {
|
||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
};
|
||||
</script>
|
||||
|
@ -218,95 +227,97 @@
|
|||
{#if messages.length == 0}
|
||||
<Placeholder models={selectedModels} modelfiles={selectedModelfiles} />
|
||||
{:else}
|
||||
{#key chatId}
|
||||
{#each messages as message, messageIdx}
|
||||
<div class=" w-full">
|
||||
<div
|
||||
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
|
||||
? 'max-w-full'
|
||||
: 'max-w-3xl'} mx-auto rounded-lg group"
|
||||
>
|
||||
{#if message.role === 'user'}
|
||||
<UserMessage
|
||||
user={$user}
|
||||
{message}
|
||||
siblings={message.parentId !== null
|
||||
? history.messages[message.parentId]?.childrenIds ?? []
|
||||
: Object.values(history.messages)
|
||||
.filter((message) => message.parentId === null)
|
||||
.map((message) => message.id) ?? []}
|
||||
{confirmEditMessage}
|
||||
{showPreviousMessage}
|
||||
{showNextMessage}
|
||||
{copyToClipboard}
|
||||
/>
|
||||
<div class=" pb-10">
|
||||
{#key chatId}
|
||||
{#each messages as message, messageIdx}
|
||||
<div class=" w-full">
|
||||
<div
|
||||
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
|
||||
? 'max-w-full'
|
||||
: 'max-w-3xl'} mx-auto rounded-lg group"
|
||||
>
|
||||
{#if message.role === 'user'}
|
||||
<UserMessage
|
||||
user={$user}
|
||||
{message}
|
||||
siblings={message.parentId !== null
|
||||
? history.messages[message.parentId]?.childrenIds ?? []
|
||||
: Object.values(history.messages)
|
||||
.filter((message) => message.parentId === null)
|
||||
.map((message) => message.id) ?? []}
|
||||
{confirmEditMessage}
|
||||
{showPreviousMessage}
|
||||
{showNextMessage}
|
||||
{copyToClipboard}
|
||||
/>
|
||||
|
||||
{#if messages.length - 1 === messageIdx && processing !== ''}
|
||||
<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
|
||||
<div class=" dark:text-blue-100">
|
||||
<svg
|
||||
class=" w-4 h-4 translate-y-[0.5px]"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.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);
|
||||
{#if messages.length - 1 === messageIdx && processing !== ''}
|
||||
<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
|
||||
<div class=" dark:text-blue-100">
|
||||
<svg
|
||||
class=" w-4 h-4 translate-y-[0.5px]"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_qM83 {
|
||||
animation: spinner_8HQG 1.05s infinite;
|
||||
}
|
||||
28.57% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||
transform: translateY(-6px);
|
||||
.spinner_oXPr {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
100% {
|
||||
transform: translate(0);
|
||||
.spinner_ZTLf {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
}
|
||||
</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
|
||||
>
|
||||
@keyframes spinner_8HQG {
|
||||
0%,
|
||||
57.14% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||
transform: translate(0);
|
||||
}
|
||||
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 class=" text-sm font-medium">
|
||||
{processing}
|
||||
</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}
|
||||
{:else}
|
||||
<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}
|
||||
<div class=" mb-10" />
|
||||
{/if}
|
||||
{/key}
|
||||
{#if bottomPadding}
|
||||
<div class=" mb-10" />
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</script>
|
||||
|
||||
{#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 -space-x-4 mb-1">
|
||||
{#each models as model, modelIdx}
|
||||
|
|
|
@ -137,6 +137,11 @@
|
|||
});
|
||||
};
|
||||
|
||||
const scrollToBottom = () => {
|
||||
const element = document.getElementById('messages-container');
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
// Ollama functions
|
||||
//////////////////////////
|
||||
|
@ -316,7 +321,7 @@
|
|||
await tick();
|
||||
|
||||
// Scroll down
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
|
||||
const messagesBody = [
|
||||
$settings.system
|
||||
|
@ -469,7 +474,7 @@
|
|||
}
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,7 +513,7 @@
|
|||
await tick();
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
if (messages.length == 2 && messages.at(1).content !== '') {
|
||||
|
@ -519,8 +524,7 @@
|
|||
|
||||
const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => {
|
||||
const responseMessage = history.messages[responseMessageId];
|
||||
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
|
||||
const res = await generateOpenAIChatCompletion(localStorage.token, {
|
||||
model: model,
|
||||
|
@ -628,7 +632,7 @@
|
|||
}
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,7 +676,7 @@
|
|||
await tick();
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
if (messages.length == 2) {
|
||||
|
@ -783,16 +787,18 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:scroll={(e) => {
|
||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="min-h-screen w-full flex flex-col">
|
||||
<div class="min-h-screen max-h-screen w-full flex flex-col">
|
||||
<Navbar {title} shareEnabled={messages.length > 0} {initNewChat} {tags} {addTag} {deleteTag} />
|
||||
<div class="flex flex-col justify-center h-full">
|
||||
<div class=" pb-2.5 flex flex-1 flex-col justify-between w-full overflow-hidden">
|
||||
<div class="flex flex-col flex-auto">
|
||||
<div
|
||||
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-80"
|
||||
id="messages-container"
|
||||
on:scroll={(e) => {
|
||||
console.log(e.target.scrollHeight, e.target.scrollTop, e.target.clientHeight);
|
||||
console.log(e.target.scrollHeight - e.target.scrollTop, e.target.clientHeight);
|
||||
autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="{$settings?.fullScreenMode ?? null
|
||||
? 'max-w-full'
|
||||
|
@ -801,7 +807,7 @@
|
|||
<ModelSelector bind:selectedModels disabled={messages.length > 0} />
|
||||
</div>
|
||||
|
||||
<div class=" h-full mt-14 w-full flex flex-col">
|
||||
<div class=" h-full w-full flex flex-col py-8">
|
||||
<Messages
|
||||
chatId={$chatId}
|
||||
{selectedModels}
|
||||
|
|
|
@ -153,6 +153,11 @@
|
|||
}
|
||||
};
|
||||
|
||||
const scrollToBottom = () => {
|
||||
const element = document.getElementById('messages-container');
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
// Ollama functions
|
||||
//////////////////////////
|
||||
|
@ -330,7 +335,7 @@
|
|||
await tick();
|
||||
|
||||
// Scroll down
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
|
||||
const messagesBody = [
|
||||
$settings.system
|
||||
|
@ -483,7 +488,7 @@
|
|||
}
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -522,7 +527,7 @@
|
|||
await tick();
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
if (messages.length == 2 && messages.at(1).content !== '') {
|
||||
|
@ -534,7 +539,7 @@
|
|||
const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => {
|
||||
const responseMessage = history.messages[responseMessageId];
|
||||
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
|
||||
const res = await generateOpenAIChatCompletion(localStorage.token, {
|
||||
model: model,
|
||||
|
@ -642,7 +647,7 @@
|
|||
}
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -686,7 +691,7 @@
|
|||
await tick();
|
||||
|
||||
if (autoScroll) {
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
if (messages.length == 2) {
|
||||
|
@ -797,14 +802,8 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:scroll={(e) => {
|
||||
autoScroll = window.innerHeight + window.scrollY >= document.body.offsetHeight - 40;
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if loaded}
|
||||
<div class="min-h-screen w-full flex flex-col">
|
||||
<div class="min-h-screen max-h-screen w-full flex flex-col">
|
||||
<Navbar
|
||||
{title}
|
||||
shareEnabled={messages.length > 0}
|
||||
|
@ -820,8 +819,16 @@
|
|||
{addTag}
|
||||
{deleteTag}
|
||||
/>
|
||||
<div class="justify-center">
|
||||
<div class=" pb-2.5 flex flex-col justify-between w-full">
|
||||
<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) => {
|
||||
console.log(e.target.scrollHeight, e.target.scrollTop, e.target.clientHeight);
|
||||
console.log(e.target.scrollHeight - e.target.scrollTop, e.target.clientHeight);
|
||||
autoScroll = e.target.scrollHeight - e.target.scrollTop <= e.target.clientHeight + 50;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="{$settings?.fullScreenMode ?? null
|
||||
? 'max-w-full'
|
||||
|
|
Loading…
Reference in a new issue