Commit f9e3c47d authored by Michael Poluektov's avatar Michael Poluektov
Browse files

rebase

parents 49b4211c 24ef5af2
...@@ -214,7 +214,7 @@ ...@@ -214,7 +214,7 @@
<div class="flex gap-2"> <div class="flex gap-2">
<div class="flex-1"> <div class="flex-1">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedPipelinesUrlIdx} bind:value={selectedPipelinesUrlIdx}
placeholder={$i18n.t('Select a pipeline url')} placeholder={$i18n.t('Select a pipeline url')}
on:change={async () => { on:change={async () => {
...@@ -328,7 +328,7 @@ ...@@ -328,7 +328,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter Github Raw URL')} placeholder={$i18n.t('Enter Github Raw URL')}
bind:value={pipelineDownloadUrl} bind:value={pipelineDownloadUrl}
/> />
...@@ -412,7 +412,7 @@ ...@@ -412,7 +412,7 @@
<div class="flex gap-2"> <div class="flex gap-2">
<div class="flex-1"> <div class="flex-1">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedPipelineIdx} bind:value={selectedPipelineIdx}
placeholder={$i18n.t('Select a pipeline')} placeholder={$i18n.t('Select a pipeline')}
on:change={async () => { on:change={async () => {
...@@ -482,7 +482,7 @@ ...@@ -482,7 +482,7 @@
<div class=" flex-1"> <div class=" flex-1">
{#if valves_spec.properties[property]?.enum ?? null} {#if valves_spec.properties[property]?.enum ?? null}
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={valves[property]} bind:value={valves[property]}
> >
{#each valves_spec.properties[property].enum as option} {#each valves_spec.properties[property].enum as option}
...@@ -503,7 +503,7 @@ ...@@ -503,7 +503,7 @@
</div> </div>
{:else} {:else}
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={valves_spec.properties[property].title} placeholder={valves_spec.properties[property].title}
bind:value={valves[property]} bind:value={valves[property]}
......
...@@ -112,7 +112,7 @@ ...@@ -112,7 +112,7 @@
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={defaultModelId} bind:value={defaultModelId}
placeholder="Select a model" placeholder="Select a model"
> >
...@@ -140,7 +140,7 @@ ...@@ -140,7 +140,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1 mr-2"> <div class="flex-1 mr-2">
<select <select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={modelId} bind:value={modelId}
placeholder="Select a model" placeholder="Select a model"
> >
......
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={$i18n.t('Enter Searxng Query URL')} placeholder={$i18n.t('Enter Searxng Query URL')}
bind:value={webConfig.search.searxng_query_url} bind:value={webConfig.search.searxng_query_url}
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={$i18n.t('Enter Google PSE Engine Id')} placeholder={$i18n.t('Enter Google PSE Engine Id')}
bind:value={webConfig.search.google_pse_engine_id} bind:value={webConfig.search.google_pse_engine_id}
...@@ -205,7 +205,7 @@ ...@@ -205,7 +205,7 @@
</div> </div>
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Search Result Count')} placeholder={$i18n.t('Search Result Count')}
bind:value={webConfig.search.result_count} bind:value={webConfig.search.result_count}
required required
...@@ -218,7 +218,7 @@ ...@@ -218,7 +218,7 @@
</div> </div>
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Concurrent Requests')} placeholder={$i18n.t('Concurrent Requests')}
bind:value={webConfig.search.concurrent_requests} bind:value={webConfig.search.concurrent_requests}
required required
...@@ -267,7 +267,7 @@ ...@@ -267,7 +267,7 @@
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div> <div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div>
<div class=" flex-1 self-center"> <div class=" flex-1 self-center">
<input <input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text" type="text"
placeholder={$i18n.t('Enter language codes')} placeholder={$i18n.t('Enter language codes')}
bind:value={youtubeLanguage} bind:value={youtubeLanguage}
......
...@@ -60,19 +60,26 @@ ...@@ -60,19 +60,26 @@
import Navbar from '$lib/components/layout/Navbar.svelte'; import Navbar from '$lib/components/layout/Navbar.svelte';
import CallOverlay from './MessageInput/CallOverlay.svelte'; import CallOverlay from './MessageInput/CallOverlay.svelte';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import ChatControls from './ChatControls.svelte';
import EventConfirmDialog from '../common/ConfirmDialog.svelte';
const i18n: Writable<i18nType> = getContext('i18n'); const i18n: Writable<i18nType> = getContext('i18n');
export let chatIdProp = ''; export let chatIdProp = '';
let loaded = false; let loaded = false;
const eventTarget = new EventTarget(); const eventTarget = new EventTarget();
let showControls = false;
let stopResponseFlag = false; let stopResponseFlag = false;
let autoScroll = true; let autoScroll = true;
let processing = ''; let processing = '';
let messagesContainerElement: HTMLDivElement; let messagesContainerElement: HTMLDivElement;
let showEventConfirmation = false;
let eventConfirmationTitle = '';
let eventConfirmationMessage = '';
let eventCallback = null;
let showModelSelector = true; let showModelSelector = true;
let selectedModels = ['']; let selectedModels = [''];
...@@ -96,6 +103,8 @@ ...@@ -96,6 +103,8 @@
currentId: null currentId: null
}; };
let params = {};
$: if (history.currentId !== null) { $: if (history.currentId !== null) {
let _messages = []; let _messages = [];
...@@ -126,21 +135,35 @@ ...@@ -126,21 +135,35 @@
})(); })();
} }
const chatEventHandler = async (data) => { const chatEventHandler = async (event, cb) => {
if (data.chat_id === $chatId) { if (event.chat_id === $chatId) {
await tick(); await tick();
console.log(data); console.log(event);
let message = history.messages[data.message_id]; let message = history.messages[event.message_id];
const status = { const type = event?.data?.type ?? null;
done: data?.data?.done ?? null, const data = event?.data?.data ?? null;
description: data?.data?.status ?? null
}; if (type === 'status') {
if (message?.statusHistory) {
message.statusHistory.push(data);
} else {
message.statusHistory = [data];
}
} else if (type === 'citation') {
if (message?.citations) {
message.citations.push(data);
} else {
message.citations = [data];
}
} else if (type === 'confirmation') {
eventCallback = cb;
showEventConfirmation = true;
if (message.statusHistory) { eventConfirmationTitle = data.title;
message.statusHistory.push(status); eventConfirmationMessage = data.message;
} else { } else {
message.statusHistory = [status]; console.log('Unknown message type', data);
} }
messages = messages; messages = messages;
...@@ -221,6 +244,7 @@ ...@@ -221,6 +244,7 @@
messages: {}, messages: {},
currentId: null currentId: null
}; };
params = {};
if ($page.url.searchParams.get('models')) { if ($page.url.searchParams.get('models')) {
selectedModels = $page.url.searchParams.get('models')?.split(','); selectedModels = $page.url.searchParams.get('models')?.split(',');
...@@ -290,11 +314,7 @@ ...@@ -290,11 +314,7 @@
await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
} }
await settings.set({ params = chatContent?.params ?? {};
...$settings,
system: chatContent.system ?? $settings.system,
params: chatContent.options ?? $settings.params
});
autoScroll = true; autoScroll = true;
await tick(); await tick();
...@@ -507,9 +527,7 @@ ...@@ -507,9 +527,7 @@
title: $i18n.t('New Chat'), title: $i18n.t('New Chat'),
models: selectedModels, models: selectedModels,
system: $settings.system ?? undefined, system: $settings.system ?? undefined,
options: { params: params,
...($settings.params ?? {})
},
messages: messages, messages: messages,
history: history, history: history,
tags: [], tags: [],
...@@ -607,11 +625,11 @@ ...@@ -607,11 +625,11 @@
scrollToBottom(); scrollToBottom();
const messagesBody = [ const messagesBody = [
$settings.system || (responseMessage?.userContext ?? null) params?.system || $settings.system || (responseMessage?.userContext ?? null)
? { ? {
role: 'system', role: 'system',
content: `${promptTemplate( content: `${promptTemplate(
$settings?.system ?? '', params?.system ?? $settings?.system ?? '',
$user.name, $user.name,
$settings?.userLocation $settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token) ? await getAndUpdateUserLocation(localStorage.token)
...@@ -696,15 +714,16 @@ ...@@ -696,15 +714,16 @@
model: model.id, model: model.id,
messages: messagesBody, messages: messagesBody,
options: { options: {
...($settings.params ?? {}), ...(params ?? $settings.params ?? {}),
stop: stop:
$settings?.params?.stop ?? undefined params?.stop ?? $settings?.params?.stop ?? undefined
? $settings.params.stop.map((str) => ? (params?.stop ?? $settings.params.stop).map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"')) decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
) )
: undefined, : undefined,
num_predict: $settings?.params?.max_tokens ?? undefined, num_predict: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined,
repeat_penalty: $settings?.params?.frequency_penalty ?? undefined repeat_penalty:
params?.frequency_penalty ?? $settings?.params?.frequency_penalty ?? undefined
}, },
format: $settings.requestFormat ?? undefined, format: $settings.requestFormat ?? undefined,
keep_alive: $settings.keepAlive ?? undefined, keep_alive: $settings.keepAlive ?? undefined,
...@@ -840,7 +859,8 @@ ...@@ -840,7 +859,8 @@
chat = await updateChatById(localStorage.token, _chatId, { chat = await updateChatById(localStorage.token, _chatId, {
messages: messages, messages: messages,
history: history, history: history,
models: selectedModels models: selectedModels,
params: params
}); });
await chats.set(await getChatList(localStorage.token)); await chats.set(await getChatList(localStorage.token));
} }
...@@ -950,11 +970,11 @@ ...@@ -950,11 +970,11 @@
} }
: undefined, : undefined,
messages: [ messages: [
$settings.system || (responseMessage?.userContext ?? null) params?.system || $settings.system || (responseMessage?.userContext ?? null)
? { ? {
role: 'system', role: 'system',
content: `${promptTemplate( content: `${promptTemplate(
$settings?.system ?? '', params?.system ?? $settings?.system ?? '',
$user.name, $user.name,
$settings?.userLocation $settings?.userLocation
? await getAndUpdateUserLocation(localStorage.token) ? await getAndUpdateUserLocation(localStorage.token)
...@@ -999,17 +1019,18 @@ ...@@ -999,17 +1019,18 @@
: message?.raContent ?? message.content : message?.raContent ?? message.content
}) })
})), })),
seed: $settings?.params?.seed ?? undefined, seed: params?.seed ?? $settings?.params?.seed ?? undefined,
stop: stop:
$settings?.params?.stop ?? undefined params?.stop ?? $settings?.params?.stop ?? undefined
? $settings.params.stop.map((str) => ? (params?.stop ?? $settings.params.stop).map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"')) decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
) )
: undefined, : undefined,
temperature: $settings?.params?.temperature ?? undefined, temperature: params?.temperature ?? $settings?.params?.temperature ?? undefined,
top_p: $settings?.params?.top_p ?? undefined, top_p: params?.top_p ?? $settings?.params?.top_p ?? undefined,
frequency_penalty: $settings?.params?.frequency_penalty ?? undefined, frequency_penalty:
max_tokens: $settings?.params?.max_tokens ?? undefined, params?.frequency_penalty ?? $settings?.params?.frequency_penalty ?? undefined,
max_tokens: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined,
tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined, tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
files: files.length > 0 ? files : undefined, files: files.length > 0 ? files : undefined,
session_id: $socket?.id, session_id: $socket?.id,
...@@ -1115,7 +1136,8 @@ ...@@ -1115,7 +1136,8 @@
chat = await updateChatById(localStorage.token, _chatId, { chat = await updateChatById(localStorage.token, _chatId, {
models: selectedModels, models: selectedModels,
messages: messages, messages: messages,
history: history history: history,
params: params
}); });
await chats.set(await getChatList(localStorage.token)); await chats.set(await getChatList(localStorage.token));
} }
...@@ -1382,6 +1404,18 @@ ...@@ -1382,6 +1404,18 @@
<audio id="audioElement" src="" style="display: none;" /> <audio id="audioElement" src="" style="display: none;" />
<EventConfirmDialog
bind:show={showEventConfirmation}
title={eventConfirmationTitle}
message={eventConfirmationMessage}
on:confirm={(e) => {
eventCallback(true);
}}
on:cancel={() => {
eventCallback(false);
}}
/>
{#if $showCallOverlay} {#if $showCallOverlay}
<CallOverlay <CallOverlay
{submitPrompt} {submitPrompt}
...@@ -1416,6 +1450,7 @@ ...@@ -1416,6 +1450,7 @@
{title} {title}
bind:selectedModels bind:selectedModels
bind:showModelSelector bind:showModelSelector
bind:showControls
shareEnabled={messages.length > 0} shareEnabled={messages.length > 0}
{chat} {chat}
{initNewChat} {initNewChat}
...@@ -1425,7 +1460,7 @@ ...@@ -1425,7 +1460,7 @@
<div <div
class="absolute top-[4.25rem] w-full {$showSidebar class="absolute top-[4.25rem] w-full {$showSidebar
? 'md:max-w-[calc(100%-260px)]' ? 'md:max-w-[calc(100%-260px)]'
: ''} z-20" : ''} {showControls ? 'lg:pr-[24rem]' : ''} z-20"
> >
<div class=" flex flex-col gap-1 w-full"> <div class=" flex flex-col gap-1 w-full">
{#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner} {#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner}
...@@ -1452,7 +1487,9 @@ ...@@ -1452,7 +1487,9 @@
<div class="flex flex-col flex-auto z-10"> <div class="flex flex-col flex-auto z-10">
<div <div
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10" class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden {showControls
? 'lg:pr-[24rem]'
: ''}"
id="messages-container" id="messages-container"
bind:this={messagesContainerElement} bind:this={messagesContainerElement}
on:scroll={(e) => { on:scroll={(e) => {
...@@ -1477,26 +1514,31 @@ ...@@ -1477,26 +1514,31 @@
/> />
</div> </div>
</div> </div>
<MessageInput
bind:files <div class={showControls ? 'lg:pr-[24rem]' : ''}>
bind:prompt <MessageInput
bind:autoScroll bind:files
bind:selectedToolIds bind:prompt
bind:webSearchEnabled bind:autoScroll
bind:atSelectedModel bind:selectedToolIds
availableToolIds={selectedModelIds.reduce((a, e, i, arr) => { bind:webSearchEnabled
const model = $models.find((m) => m.id === e); bind:atSelectedModel
if (model?.info?.meta?.toolIds ?? false) { availableToolIds={selectedModelIds.reduce((a, e, i, arr) => {
return [...new Set([...a, ...model.info.meta.toolIds])]; const model = $models.find((m) => m.id === e);
} if (model?.info?.meta?.toolIds ?? false) {
return a; return [...new Set([...a, ...model.info.meta.toolIds])];
}, [])} }
transparentBackground={$settings?.backgroundImageUrl ?? false} return a;
{selectedModels} }, [])}
{messages} transparentBackground={$settings?.backgroundImageUrl ?? false}
{submitPrompt} {selectedModels}
{stopResponse} {messages}
/> {submitPrompt}
{stopResponse}
/>
</div>
</div> </div>
<ChatControls bind:show={showControls} bind:params />
</div> </div>
{/if} {/if}
<script lang="ts">
import { slide } from 'svelte/transition';
import Modal from '../common/Modal.svelte';
import Controls from './Controls/Controls.svelte';
import { onMount } from 'svelte';
export let show = false;
export let chatId = null;
export let params = {};
let largeScreen = false;
onMount(() => {
// listen to resize 1024px
const mediaQuery = window.matchMedia('(min-width: 1024px)');
const handleMediaQuery = (e) => {
if (e.matches) {
largeScreen = true;
} else {
largeScreen = false;
}
};
mediaQuery.addEventListener('change', handleMediaQuery);
handleMediaQuery(mediaQuery);
return () => {
mediaQuery.removeEventListener('change', handleMediaQuery);
};
});
</script>
{#if largeScreen}
{#if show}
<div class=" absolute bottom-0 right-0 z-20 h-full pointer-events-none">
<div class="pr-4 pt-14 pb-8 w-[24rem] h-full" in:slide={{ duration: 200, axis: 'x' }}>
<div
class="w-full h-full px-5 py-4 bg-white dark:shadow-lg dark:bg-gray-850 border border-gray-50 dark:border-gray-800 rounded-xl z-50 pointer-events-auto overflow-y-auto scrollbar-hidden"
>
<Controls
on:close={() => {
show = false;
}}
bind:params
/>
</div>
</div>
</div>
{/if}
{:else}
<Modal bind:show>
<div class=" px-6 py-4 h-full">
<Controls
on:close={() => {
show = false;
}}
bind:params
/>
</div>
</Modal>
{/if}
<script>
import { createEventDispatcher, getContext } from 'svelte';
const dispatch = createEventDispatcher();
const i18n = getContext('i18n');
import XMark from '$lib/components/icons/XMark.svelte';
import AdvancedParams from '../Settings/Advanced/AdvancedParams.svelte';
export let params = {};
</script>
<div class=" dark:text-white">
<div class=" flex justify-between dark:text-gray-100 mb-2">
<div class=" text-lg font-medium self-center font-primary">{$i18n.t('Chat Controls')}</div>
<button
class="self-center"
on:click={() => {
dispatch('close');
}}
>
<XMark className="size-4" />
</button>
</div>
<div class=" dark:text-gray-200 text-sm font-primary">
<div>
<div class="mb-1.5 font-medium">System Prompt</div>
<div>
<textarea
bind:value={params.system}
class="w-full rounded-lg px-4 py-3 text-sm dark:text-gray-300 dark:bg-gray-850 border border-gray-100 dark:border-gray-800 outline-none resize-none"
rows="3"
placeholder="Enter system prompt"
/>
</div>
</div>
<hr class="my-2 border-gray-100 dark:border-gray-800" />
<div>
<div class="mb-1.5 font-medium">Advanced Params</div>
<div>
<AdvancedParams bind:params />
</div>
</div>
</div>
</div>
...@@ -316,7 +316,7 @@ ...@@ -316,7 +316,7 @@
</div> </div>
{/if} {/if}
<div class="w-full"> <div class="w-full font-primary">
<div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center"> <div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
<div class="flex flex-col max-w-6xl px-2.5 md:px-6 w-full"> <div class="flex flex-col max-w-6xl px-2.5 md:px-6 w-full">
<div class="relative"> <div class="relative">
......
...@@ -662,10 +662,11 @@ ...@@ -662,10 +662,11 @@
: rmsLevel * 100 > 1 : rmsLevel * 100 > 1
? 'size-14' ? 'size-14'
: 'size-12'} transition-all rounded-full {(model?.info?.meta : 'size-12'} transition-all rounded-full {(model?.info?.meta
?.profile_image_url ?? '/favicon.png') !== '/favicon.png' ?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
? ' bg-cover bg-center bg-no-repeat' ? ' bg-cover bg-center bg-no-repeat'
: 'bg-black dark:bg-white'} bg-black dark:bg-white" : 'bg-black dark:bg-white'} bg-black dark:bg-white"
style={(model?.info?.meta?.profile_image_url ?? '/favicon.png') !== '/favicon.png' style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
'/static/favicon.png'
? `background-image: url('${model?.info?.meta?.profile_image_url}');` ? `background-image: url('${model?.info?.meta?.profile_image_url}');`
: ''} : ''}
/> />
...@@ -743,10 +744,11 @@ ...@@ -743,10 +744,11 @@
: rmsLevel * 100 > 1 : rmsLevel * 100 > 1
? 'size-[11.5rem]' ? 'size-[11.5rem]'
: 'size-44'} transition-all rounded-full {(model?.info?.meta : 'size-44'} transition-all rounded-full {(model?.info?.meta
?.profile_image_url ?? '/favicon.png') !== '/favicon.png' ?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
? ' bg-cover bg-center bg-no-repeat' ? ' bg-cover bg-center bg-no-repeat'
: 'bg-black dark:bg-white'} " : 'bg-black dark:bg-white'} "
style={(model?.info?.meta?.profile_image_url ?? '/favicon.png') !== '/favicon.png' style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
'/static/favicon.png'
? `background-image: url('${model?.info?.meta?.profile_image_url}');` ? `background-image: url('${model?.info?.meta?.profile_image_url}');`
: ''} : ''}
/> />
......
...@@ -250,7 +250,8 @@ __builtins__.input = input`); ...@@ -250,7 +250,8 @@ __builtins__.input = input`);
stderr || stderr ||
result) && result) &&
'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code 'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code
class="language-{lang} rounded-t-none whitespace-pre">{@html highlightedCode || code}</code class="language-{lang} rounded-t-none whitespace-pre"
>{#if highlightedCode}{@html highlightedCode}{:else}{code}{/if}</code
></pre> ></pre>
<div <div
......
<div class=" self-center font-bold mb-0.5 line-clamp-1 contents"> <div class=" self-center font-semibold mb-0.5 line-clamp-1 contents">
<slot /> <slot />
</div> </div>
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
</div> </div>
<div <div
class=" mt-2 mb-4 text-3xl text-gray-800 dark:text-gray-100 font-semibold text-left flex items-center gap-4" class=" mt-2 mb-4 text-3xl text-gray-800 dark:text-gray-100 font-semibold text-left flex items-center gap-4 font-primary"
> >
<div> <div>
<div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}> <div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}>
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
</div> </div>
{/if} {/if}
{:else} {:else}
<div class=" font-medium text-gray-400 dark:text-gray-500 line-clamp-1"> <div class=" font-medium text-gray-400 dark:text-gray-500 line-clamp-1 font-p">
{$i18n.t('How can I help you today?')} {$i18n.t('How can I help you today?')}
</div> </div>
{/if} {/if}
...@@ -110,7 +110,7 @@ ...@@ -110,7 +110,7 @@
</div> </div>
</div> </div>
<div class=" w-full" in:fade={{ duration: 200, delay: 300 }}> <div class=" w-full font-primary" in:fade={{ duration: 200, delay: 300 }}>
<Suggestions <Suggestions
suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ?? suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
$config.default_prompt_suggestions} $config.default_prompt_suggestions}
......
...@@ -152,7 +152,10 @@ ...@@ -152,7 +152,10 @@
} }
tooltipInstance = tippy(`#info-${message.id}`, { tooltipInstance = tippy(`#info-${message.id}`, {
content: `<span class="text-xs" id="tooltip-${message.id}">${tooltipContent}</span>`, content: `<span class="text-xs" id="tooltip-${message.id}">${tooltipContent}</span>`,
allowHTML: true allowHTML: true,
theme: 'dark',
arrow: false,
offset: [0, 4]
}); });
} }
}; };
......
...@@ -2,32 +2,25 @@ ...@@ -2,32 +2,25 @@
import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
import ChevronUp from '$lib/components/icons/ChevronUp.svelte'; import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte'; import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
import { Collapsible } from 'bits-ui'; import Collapsible from '$lib/components/common/Collapsible.svelte';
import { slide } from 'svelte/transition';
export let status = { urls: [], query: '' }; export let status = { urls: [], query: '' };
let state = false; let state = false;
</script> </script>
<Collapsible.Root class="w-full space-y-1" bind:open={state}> <Collapsible bind:open={state} className="w-full space-y-1">
<Collapsible.Trigger> <div
<div class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
>
<slot />
{#if state}
<ChevronUp strokeWidth="3.5" className="size-3.5 " />
{:else}
<ChevronDown strokeWidth="3.5" className="size-3.5 " />
{/if}
</div>
</Collapsible.Trigger>
<Collapsible.Content
class=" text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl"
transition={slide}
> >
<slot />
{#if state}
<ChevronUp strokeWidth="3.5" className="size-3.5 " />
{:else}
<ChevronDown strokeWidth="3.5" className="size-3.5 " />
{/if}
</div>
<div class="text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl" slot="content">
{#if status?.query} {#if status?.query}
<a <a
href="https://www.google.com/search?q={status.query}" href="https://www.google.com/search?q={status.query}"
...@@ -93,5 +86,5 @@ ...@@ -93,5 +86,5 @@
</div> </div>
</a> </a>
{/each} {/each}
</Collapsible.Content> </div>
</Collapsible.Root> </Collapsible>
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
} }
</script> </script>
<div class="flex flex-col w-full items-center md:items-start"> <div class="flex flex-col w-full items-start">
{#each selectedModels as selectedModel, selectedModelIdx} {#each selectedModels as selectedModel, selectedModelIdx}
<div class="flex w-full max-w-fit"> <div class="flex w-full max-w-fit">
<div class="overflow-hidden w-full"> <div class="overflow-hidden w-full">
...@@ -103,7 +103,7 @@ ...@@ -103,7 +103,7 @@
</div> </div>
{#if showSetDefault && !$mobile} {#if showSetDefault && !$mobile}
<div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500"> <div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500 font-primary">
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button> <button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
</div> </div>
{/if} {/if}
...@@ -206,7 +206,7 @@ ...@@ -206,7 +206,7 @@
}} }}
closeFocus={false} closeFocus={false}
> >
<DropdownMenu.Trigger class="relative w-full" aria-label={placeholder}> <DropdownMenu.Trigger class="relative w-full font-primary" aria-label={placeholder}>
<div <div
class="flex w-full text-left px-0.5 outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none" class="flex w-full text-left px-0.5 outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none"
> >
...@@ -222,7 +222,7 @@ ...@@ -222,7 +222,7 @@
<DropdownMenu.Content <DropdownMenu.Content
class=" z-40 {$mobile class=" z-40 {$mobile
? `w-full` ? `w-full`
: `${className}`} max-w-[calc(100vw-1rem)] justify-start rounded-xl bg-white dark:bg-gray-850 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-850/50 outline-none " : `${className}`} max-w-[calc(100vw-1rem)] justify-start rounded-xl bg-white dark:bg-gray-850 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/40 outline-none"
transition={flyAndScale} transition={flyAndScale}
side={$mobile ? 'bottom' : 'bottom-start'} side={$mobile ? 'bottom' : 'bottom-start'}
sideOffset={4} sideOffset={4}
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
<div class="flex gap-0.5 self-start h-full mb-0.5 -translate-x-1"> <div class="flex gap-0.5 self-start h-full mb-0.5 -translate-x-1">
{#each item.model?.info?.meta.tags as tag} {#each item.model?.info?.meta.tags as tag}
<div <div
class=" text-xs font-black px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200" class=" text-xs font-bold px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
> >
{tag.name} {tag.name}
</div> </div>
...@@ -299,7 +299,7 @@ ...@@ -299,7 +299,7 @@
<div class="flex gap-0.5 self-center items-center h-full translate-y-[0.5px]"> <div class="flex gap-0.5 self-center items-center h-full translate-y-[0.5px]">
{#each item.model?.info?.meta.tags as tag} {#each item.model?.info?.meta.tags as tag}
<div <div
class=" text-xs font-black px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200" class=" text-xs font-bold px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
> >
{tag.name} {tag.name}
</div> </div>
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Seed')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Seed')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.seed = (params?.seed ?? null) === null ? 0 : null; params.seed = (params?.seed ?? null) === null ? 0 : null;
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Stop Sequence')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Stop Sequence')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.stop = (params?.stop ?? null) === null ? '' : null; params.stop = (params?.stop ?? null) === null ? '' : null;
...@@ -113,7 +113,7 @@ ...@@ -113,7 +113,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Temperature')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Temperature')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.temperature = (params?.temperature ?? null) === null ? 0.8 : null; params.temperature = (params?.temperature ?? null) === null ? 0.8 : null;
...@@ -159,7 +159,7 @@ ...@@ -159,7 +159,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Mirostat')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.mirostat = (params?.mirostat ?? null) === null ? 0 : null; params.mirostat = (params?.mirostat ?? null) === null ? 0 : null;
...@@ -205,7 +205,7 @@ ...@@ -205,7 +205,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Eta')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Eta')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.mirostat_eta = (params?.mirostat_eta ?? null) === null ? 0.1 : null; params.mirostat_eta = (params?.mirostat_eta ?? null) === null ? 0.1 : null;
...@@ -251,7 +251,7 @@ ...@@ -251,7 +251,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Tau')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Tau')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.mirostat_tau = (params?.mirostat_tau ?? null) === null ? 5.0 : null; params.mirostat_tau = (params?.mirostat_tau ?? null) === null ? 5.0 : null;
...@@ -297,7 +297,7 @@ ...@@ -297,7 +297,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.top_k = (params?.top_k ?? null) === null ? 40 : null; params.top_k = (params?.top_k ?? null) === null ? 40 : null;
...@@ -343,7 +343,7 @@ ...@@ -343,7 +343,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Top P')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Top P')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.top_p = (params?.top_p ?? null) === null ? 0.9 : null; params.top_p = (params?.top_p ?? null) === null ? 0.9 : null;
...@@ -389,7 +389,7 @@ ...@@ -389,7 +389,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Frequency Penalty')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Frequency Penalty')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.frequency_penalty = (params?.frequency_penalty ?? null) === null ? 1.1 : null; params.frequency_penalty = (params?.frequency_penalty ?? null) === null ? 1.1 : null;
...@@ -435,7 +435,7 @@ ...@@ -435,7 +435,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Repeat Last N')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Repeat Last N')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.repeat_last_n = (params?.repeat_last_n ?? null) === null ? 64 : null; params.repeat_last_n = (params?.repeat_last_n ?? null) === null ? 64 : null;
...@@ -481,7 +481,7 @@ ...@@ -481,7 +481,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Tfs Z')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Tfs Z')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.tfs_z = (params?.tfs_z ?? null) === null ? 1 : null; params.tfs_z = (params?.tfs_z ?? null) === null ? 1 : null;
...@@ -527,7 +527,7 @@ ...@@ -527,7 +527,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Context Length')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Context Length')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.num_ctx = (params?.num_ctx ?? null) === null ? 2048 : null; params.num_ctx = (params?.num_ctx ?? null) === null ? 2048 : null;
...@@ -572,7 +572,7 @@ ...@@ -572,7 +572,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Batch Size (num_batch)')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Batch Size (num_batch)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.num_batch = (params?.num_batch ?? null) === null ? 512 : null; params.num_batch = (params?.num_batch ?? null) === null ? 512 : null;
...@@ -619,7 +619,7 @@ ...@@ -619,7 +619,7 @@
</div> </div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.num_keep = (params?.num_keep ?? null) === null ? 24 : null; params.num_keep = (params?.num_keep ?? null) === null ? 24 : null;
...@@ -664,7 +664,7 @@ ...@@ -664,7 +664,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens (num_predict)')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens (num_predict)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.max_tokens = (params?.max_tokens ?? null) === null ? 128 : null; params.max_tokens = (params?.max_tokens ?? null) === null ? 128 : null;
...@@ -711,7 +711,7 @@ ...@@ -711,7 +711,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('use_mmap (Ollama)')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('use_mmap (Ollama)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.use_mmap = (params?.use_mmap ?? null) === null ? true : null; params.use_mmap = (params?.use_mmap ?? null) === null ? true : null;
...@@ -731,7 +731,7 @@ ...@@ -731,7 +731,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('use_mlock (Ollama)')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('use_mlock (Ollama)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.use_mlock = (params?.use_mlock ?? null) === null ? true : null; params.use_mlock = (params?.use_mlock ?? null) === null ? true : null;
...@@ -751,7 +751,7 @@ ...@@ -751,7 +751,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('num_thread (Ollama)')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('num_thread (Ollama)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.num_thread = (params?.num_thread ?? null) === null ? 2 : null; params.num_thread = (params?.num_thread ?? null) === null ? 2 : null;
...@@ -797,7 +797,7 @@ ...@@ -797,7 +797,7 @@
<div class=" self-center text-xs font-medium">{$i18n.t('Template')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Template')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
type="button" type="button"
on:click={() => { on:click={() => {
params.template = (params?.template ?? null) === null ? '' : null; params.template = (params?.template ?? null) === null ? '' : null;
......
...@@ -95,6 +95,8 @@ ...@@ -95,6 +95,8 @@
} }
if (themeToApply === 'dark' && !_theme.includes('oled')) { if (themeToApply === 'dark' && !_theme.includes('oled')) {
document.documentElement.style.setProperty('--color-gray-800', '#333');
document.documentElement.style.setProperty('--color-gray-850', '#262626');
document.documentElement.style.setProperty('--color-gray-900', '#171717'); document.documentElement.style.setProperty('--color-gray-900', '#171717');
document.documentElement.style.setProperty('--color-gray-950', '#0d0d0d'); document.documentElement.style.setProperty('--color-gray-950', '#0d0d0d');
} }
...@@ -118,6 +120,8 @@ ...@@ -118,6 +120,8 @@
theme.set(_theme); theme.set(_theme);
localStorage.setItem('theme', _theme); localStorage.setItem('theme', _theme);
if (_theme.includes('oled')) { if (_theme.includes('oled')) {
document.documentElement.style.setProperty('--color-gray-800', '#101010');
document.documentElement.style.setProperty('--color-gray-850', '#050505');
document.documentElement.style.setProperty('--color-gray-900', '#000000'); document.documentElement.style.setProperty('--color-gray-900', '#000000');
document.documentElement.style.setProperty('--color-gray-950', '#000000'); document.documentElement.style.setProperty('--color-gray-950', '#000000');
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
......
<script lang="ts"> <script lang="ts">
import { getContext } from 'svelte'; import { getContext, tick } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { models, settings, user } from '$lib/stores'; import { models, settings, user } from '$lib/stores';
import { updateUserSettings } from '$lib/apis/users';
import { getModels as _getModels } from '$lib/apis'; import { getModels as _getModels } from '$lib/apis';
import { goto } from '$app/navigation';
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
import Account from './Settings/Account.svelte'; import Account from './Settings/Account.svelte';
...@@ -14,8 +15,6 @@ ...@@ -14,8 +15,6 @@
import Chats from './Settings/Chats.svelte'; import Chats from './Settings/Chats.svelte';
import User from '../icons/User.svelte'; import User from '../icons/User.svelte';
import Personalization from './Settings/Personalization.svelte'; import Personalization from './Settings/Personalization.svelte';
import { updateUserSettings } from '$lib/apis/users';
import { goto } from '$app/navigation';
import Valves from './Settings/Valves.svelte'; import Valves from './Settings/Valves.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -34,6 +33,37 @@ ...@@ -34,6 +33,37 @@
}; };
let selectedTab = 'general'; let selectedTab = 'general';
// Function to handle sideways scrolling
const scrollHandler = (event) => {
const settingsTabsContainer = document.getElementById('settings-tabs-container');
if (settingsTabsContainer) {
event.preventDefault(); // Prevent default vertical scrolling
settingsTabsContainer.scrollLeft += event.deltaY; // Scroll sideways
}
};
const addScrollListener = async () => {
await tick();
const settingsTabsContainer = document.getElementById('settings-tabs-container');
if (settingsTabsContainer) {
settingsTabsContainer.addEventListener('wheel', scrollHandler);
}
};
const removeScrollListener = async () => {
await tick();
const settingsTabsContainer = document.getElementById('settings-tabs-container');
if (settingsTabsContainer) {
settingsTabsContainer.removeEventListener('wheel', scrollHandler);
}
};
$: if (show) {
addScrollListener();
} else {
removeScrollListener();
}
</script> </script>
<Modal bind:show> <Modal bind:show>
...@@ -61,6 +91,7 @@ ...@@ -61,6 +91,7 @@
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4"> <div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
<div <div
id="settings-tabs-container"
class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0" class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
> >
<button <button
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
<div class=" flex flex-col md:flex-row md:items-center flex-1 text-sm w-fit gap-1.5"> <div class=" flex flex-col md:flex-row md:items-center flex-1 text-sm w-fit gap-1.5">
<div class="flex justify-between self-start"> <div class="flex justify-between self-start">
<div <div
class=" text-xs font-black {classNames[banner.type] ?? class=" text-xs font-bold {classNames[banner.type] ??
classNames['info']} w-fit px-2 rounded uppercase line-clamp-1 mr-0.5" classNames['info']} w-fit px-2 rounded uppercase line-clamp-1 mr-0.5"
> >
{banner.type} {banner.type}
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
{#if banner.url} {#if banner.url}
<div class="flex md:hidden group w-fit md:items-center"> <div class="flex md:hidden group w-fit md:items-center">
<a <a
class="text-gray-700 dark:text-white text-xs font-bold underline" class="text-gray-700 dark:text-white text-xs font-semibold underline"
href="/assets/files/whitepaper.pdf" href="/assets/files/whitepaper.pdf"
target="_blank">Learn More</a target="_blank">Learn More</a
> >
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
{#if banner.url} {#if banner.url}
<div class="hidden md:flex group w-fit md:items-center"> <div class="hidden md:flex group w-fit md:items-center">
<a <a
class="text-gray-700 dark:text-white text-xs font-bold underline" class="text-gray-700 dark:text-white text-xs font-semibold underline"
href="/" href="/"
target="_blank">Learn More</a target="_blank">Learn More</a
> >
...@@ -116,7 +116,8 @@ ...@@ -116,7 +116,8 @@
on:click={() => { on:click={() => {
dismiss(banner.id); dismiss(banner.id);
}} }}
class=" -mt-[3px] ml-1.5 mr-1 text-gray-400 dark:hover:text-white h-1">&times;</button class=" -mt-1 -mb-2 -translate-y-[1px] ml-1.5 mr-1 text-gray-400 dark:hover:text-white"
>&times;</button
> >
{/if} {/if}
</div> </div>
......
<script lang="ts">
import { slide } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
export let open = false;
export let className = '';
</script>
<div class={className}>
<button on:click={() => (open = !open)}>
<slot />
</button>
{#if open}
<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
<slot name="content" />
</div>
{/if}
</div>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment