This commit is contained in:
Timothy Jaeryang Baek 2026-07-14 14:17:59 -04:00
parent 3be493f5b0
commit 8dd862d338
11 changed files with 162 additions and 134 deletions

View file

@ -45,9 +45,9 @@
</script>
<div id="tab-about" class="flex flex-col h-full justify-between space-y-3 text-sm">
<div class=" space-y-3 overflow-y-scroll max-h-[28rem] md:max-h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5 space-y-3">
<div>
<div class=" mb-2.5 text-sm font-normal flex space-x-2 items-center">
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2.5 flex space-x-2 items-center">
<div>
{$WEBUI_NAME}
{$i18n.t('Version')}
@ -101,7 +101,9 @@
<hr class=" border-gray-100/30 dark:border-gray-850/30" />
<div>
<div class=" mb-2.5 text-sm font-normal">{$i18n.t('Ollama Version')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2.5">
{$i18n.t('Ollama Version')}
</div>
<div class="flex w-full">
<div class="flex-1 text-xs text-gray-700 dark:text-gray-200">
{ollamaVersion ?? 'N/A'}

View file

@ -125,10 +125,12 @@
</script>
<div id="tab-account" class="flex flex-col h-full justify-between text-sm">
<div class=" overflow-y-scroll max-h-[28rem] md:max-h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5">
<div class="space-y-1">
<div>
<div class="text-base font-normal">{$i18n.t('Your Account')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
{$i18n.t('Your Account')}
</div>
<div class="text-xs text-gray-500 mt-0.5">
{$i18n.t('Manage your account information.')}
@ -425,7 +427,7 @@
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-normal">
<div class="shrink-0 flex justify-end pt-3 text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
on:click={async () => {

View file

@ -173,44 +173,43 @@
}}
/>
<div id="tab-archived-chats" class="flex flex-col h-full justify-between text-sm">
<div class="space-y-3 overflow-y-scroll max-h-[28rem] md:max-h-full">
<div id="tab-archived-chats" class="flex flex-col h-full text-sm">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5">
<div>
<div class="flex items-center gap-2 text-base font-normal">
{$i18n.t('Archived Chats')}
<span class="text-base font-normal text-gray-500 dark:text-gray-500">
{chatCount ?? ''}
</span>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
{$i18n.t('Conversations')}
{#if chatCount !== null}
<span>{chatCount}</span>
{/if}
</div>
<div class="text-xs text-gray-500 mt-0.5">
{$i18n.t('Manage conversations you have archived.')}
<div class="py-0.5 flex w-full justify-between">
<div class="self-center text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Search')}</div>
<div class="flex min-w-0 items-center justify-end gap-1">
<input
class="w-40 bg-transparent py-1 text-right text-xs outline-hidden placeholder:text-gray-400 dark:placeholder:text-gray-600"
bind:value={query}
on:input={searchHandler}
placeholder={$i18n.t('Search')}
maxlength="500"
/>
{#if query}
<button
class="rounded-sm p-1 text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-850"
type="button"
aria-label={$i18n.t('Clear search')}
on:click={() => {
query = '';
searchHandler();
}}
>
<XMark className="size-3" strokeWidth="2" />
</button>
{/if}
</div>
</div>
</div>
<div class="flex items-center gap-2 rounded-xl bg-gray-100 dark:bg-gray-850 px-2">
<input
class="w-full bg-transparent py-1.5 text-xs outline-hidden placeholder:text-gray-400 dark:placeholder:text-gray-600"
bind:value={query}
on:input={searchHandler}
placeholder={$i18n.t('Search Chats')}
maxlength="500"
/>
{#if query}
<button
class="rounded-full p-1 text-gray-500 hover:bg-gray-200 dark:hover:bg-gray-800"
type="button"
aria-label={$i18n.t('Clear search')}
on:click={() => {
query = '';
searchHandler();
}}
>
<XMark className="size-3" strokeWidth="2" />
</button>
{/if}
</div>
{#if chatList === null}
<div class="flex min-h-20 items-center justify-center">
<Spinner className="size-5" />
@ -222,9 +221,9 @@
{$i18n.t('You have no archived conversations.')}
</div>
{:else}
<div class="flex items-center px-1 text-xs text-gray-500 dark:text-gray-500">
<div class="mt-3 flex items-center text-xs text-gray-400 dark:text-gray-600">
<button
class="flex flex-1 items-center gap-1 py-1 text-left"
class="flex flex-1 items-center gap-1 py-0.5 text-left"
type="button"
on:click={() => setSortKey('title')}
>
@ -238,7 +237,7 @@
{/if}
</button>
<button
class="hidden w-24 items-center justify-end gap-1 py-1 text-right sm:flex"
class="hidden w-24 items-center justify-end gap-1 py-0.5 text-right sm:flex"
type="button"
on:click={() => setSortKey('updated_at')}
>
@ -253,11 +252,11 @@
</button>
</div>
<div class="divide-y divide-gray-50 dark:divide-gray-850/80">
<div>
{#each chatList as chat (chat.id)}
<div class="flex items-center gap-2 py-2 text-xs">
<div class="py-0.5 flex w-full justify-between gap-2 text-xs">
<a
class="min-w-0 flex-1 truncate text-gray-800 hover:text-black dark:text-gray-200 dark:hover:text-white"
class="min-w-0 flex-1 self-center truncate text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
href={`/c/${chat.id}`}
on:click={(event) => {
event.preventDefault();
@ -267,7 +266,9 @@
>
{chat?.title}
</a>
<div class="hidden w-24 shrink-0 justify-end text-gray-500 dark:text-gray-500 sm:flex">
<div
class="hidden w-24 shrink-0 self-center justify-end text-gray-400 dark:text-gray-600 sm:flex"
>
{$i18n.t(
dayjs(chat?.updated_at * 1000).calendar(null, {
sameDay: '[Today]',
@ -279,10 +280,10 @@
})
)}
</div>
<div class="flex shrink-0 items-center text-gray-600 dark:text-gray-300">
<div class="flex shrink-0 items-center justify-end text-gray-500 dark:text-gray-500">
<Tooltip content={$i18n.t('Unarchive Chat')}>
<button
class="rounded-lg p-1 hover:bg-gray-100 dark:hover:bg-gray-850"
class="rounded-sm p-1 hover:bg-gray-100 dark:hover:bg-gray-850"
type="button"
aria-label={$i18n.t('Unarchive Chat')}
on:click={() => {
@ -307,7 +308,7 @@
</Tooltip>
<Tooltip content={$i18n.t('Delete Chat')}>
<button
class="rounded-lg p-1 hover:bg-gray-100 dark:hover:bg-gray-850"
class="rounded-sm p-1 hover:bg-gray-100 dark:hover:bg-gray-850"
type="button"
aria-label={$i18n.t('Delete Chat')}
on:click={() => {
@ -353,10 +354,10 @@
{/if}
</div>
<div class="flex justify-end gap-1.5 pt-3 text-sm font-normal">
<div class="shrink-0 pt-3 flex justify-end gap-1.5 text-sm font-normal">
{#if query === ''}
<button
class="px-3.5 py-1.5 text-sm font-normal hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-full disabled:opacity-50"
class="px-3.5 py-1.5 font-normal hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl disabled:opacity-50"
disabled={loading || chatCount === 0}
type="button"
on:click={() => {

View file

@ -175,9 +175,11 @@
dispatch('save');
}}
>
<div class=" space-y-3 overflow-y-scroll max-h-[28rem] md:max-h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5 space-y-3">
<div>
<div class=" mb-1 text-sm font-normal">{$i18n.t('STT Settings')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
{$i18n.t('STT Settings')}
</div>
{#if $config.audio.stt.engine !== 'web'}
<div class=" py-0.5 flex w-full justify-between">
@ -241,7 +243,9 @@
</div>
<div>
<div class=" mb-1 text-sm font-normal">{$i18n.t('TTS Settings')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
{$i18n.t('TTS Settings')}
</div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-normal">{$i18n.t('Text-to-Speech Engine')}</div>
@ -320,7 +324,9 @@
{#if TTSEngine === 'browser-kokoro'}
{#if TTSModel}
<div>
<div class=" mb-2.5 text-sm font-normal">{$i18n.t('Set Voice')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2.5">
{$i18n.t('Set Voice')}
</div>
<div class="flex w-full">
<div class="flex-1">
<input
@ -412,7 +418,7 @@
{/if}
</div>
<div class="flex justify-end text-sm font-normal">
<div class="shrink-0 flex justify-end text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"

View file

@ -76,13 +76,15 @@
updateHandler();
}}
>
<div class=" overflow-y-scroll scrollbar-hidden h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5">
{#if config !== null}
<div class="">
<div class="pr-1.5">
<div class="">
<div class="flex justify-between items-center mb-0.5">
<div class="font-normal">{$i18n.t('Manage Direct Connections')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600">
{$i18n.t('Manage Direct Connections')}
</div>
<Tooltip content={$i18n.t(`Add Connection`)}>
<button
@ -151,7 +153,7 @@
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-normal">
<div class="shrink-0 flex justify-end pt-3 text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"

View file

@ -132,8 +132,8 @@
}}
/>
<div id="tab-chats" class="flex flex-col h-full justify-between text-sm">
<div class="space-y-3 overflow-y-scroll max-h-[28rem] md:max-h-full">
<div id="tab-chats" class="flex flex-col h-full text-sm">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5 space-y-3">
<input
id="chat-import-input"
bind:this={chatImportInputElement}
@ -144,7 +144,7 @@
/>
<div>
<div class="mb-1 text-sm font-normal">{$i18n.t('Chats')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">{$i18n.t('Chats')}</div>
{#if $user?.role === 'admin' || ($user.permissions?.chat?.import ?? true)}
<div>
@ -227,7 +227,7 @@
</div>
<div>
<div class="mb-1 text-sm font-normal">{$i18n.t('Files')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">{$i18n.t('Files')}</div>
<div>
<div class="py-0.5 flex w-full justify-between">

View file

@ -206,9 +206,11 @@
</script>
<div class="flex flex-col h-full justify-between text-sm" id="tab-general">
<div class=" overflow-y-scroll max-h-[28rem] md:max-h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5">
<div class="">
<div class=" mb-1 text-sm font-normal">{$i18n.t('WebUI Settings')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 mb-2">
{$i18n.t('WebUI Settings')}
</div>
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-normal">{$i18n.t('Theme')}</div>
@ -297,7 +299,9 @@
<hr class="border-gray-100/30 dark:border-gray-850/30 my-3" />
<div>
<div class=" my-2.5 text-sm font-normal">{$i18n.t('System Prompt')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600 my-2.5">
{$i18n.t('System Prompt')}
</div>
<Textarea
bind:value={system}
className={'w-full text-sm outline-hidden resize-vertical' +
@ -313,7 +317,9 @@
{#if $user?.role === 'admin' || (($user?.permissions.chat?.controls ?? true) && ($user?.permissions.chat?.params ?? true))}
<div class="mt-2 space-y-3 pr-1.5">
<div class="flex justify-between items-center text-sm">
<div class=" font-normal">{$i18n.t('Advanced Parameters')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600">
{$i18n.t('Advanced Parameters')}
</div>
<button
class=" text-xs font-normal {($settings?.highContrastMode ?? false)
? 'text-gray-800 dark:text-gray-100'
@ -333,7 +339,7 @@
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-normal">
<div class="shrink-0 flex justify-end pt-3 text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
on:click={() => {

View file

@ -80,13 +80,15 @@
updateHandler();
}}
>
<div class="overflow-y-scroll scrollbar-hidden h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5">
{#if servers !== null}
<div>
<div class="pr-1.5">
<div class="">
<div class="flex justify-between items-center mb-0.5">
<div class="font-normal">{$i18n.t('Manage Tool Servers')}</div>
<div class="text-xs text-gray-400 dark:text-gray-600">
{$i18n.t('Manage Tool Servers')}
</div>
<Tooltip content={$i18n.t('Add Connection')}>
<button
@ -169,7 +171,7 @@
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-normal">
<div class="shrink-0 flex justify-end pt-3 text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"

View file

@ -335,9 +335,9 @@
}}
/>
<div class=" space-y-3 overflow-y-scroll max-h-[28rem] md:max-h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5 space-y-3">
<div>
<h1 class=" mb-2 text-sm font-normal">{$i18n.t('UI')}</h1>
<h3 class="text-xs text-gray-400 dark:text-gray-600 mb-2">{$i18n.t('UI')}</h3>
<div>
<div class="py-0.5 flex w-full justify-between">
@ -1438,7 +1438,7 @@
</div>
</div>
<div class="flex justify-end text-sm font-normal">
<div class="shrink-0 flex justify-end text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"

View file

@ -29,7 +29,7 @@
dispatch('save');
}}
>
<div class="py-1 overflow-y-scroll max-h-[28rem] md:max-h-full">
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5 -mr-1.5 py-1">
<div>
<div class="flex items-center justify-between mb-1">
<Tooltip
@ -37,7 +37,7 @@
'This is an experimental feature, it may not function as expected and is subject to change at any time.'
)}
>
<div class="flex items-center gap-2 text-sm font-normal">
<div class="flex items-center gap-2 text-xs text-gray-400 dark:text-gray-600">
{$i18n.t('Memory')}
<span
class="text-[0.65rem] font-normal uppercase px-1.5 py-0.5 rounded-full bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400"
@ -78,7 +78,7 @@
</div>
</div>
<div class="flex justify-end text-sm font-normal">
<div class="shrink-0 flex justify-end text-sm font-normal">
<button
class="px-3.5 py-1.5 text-sm font-normal bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
type="submit"

View file

@ -577,6 +577,7 @@
}`;
let selectedTab = 'general';
$: selectedTabTitle = allSettings.find((tab) => tab.id === selectedTab)?.title ?? 'Settings';
onMount(() => {
availableSettings = getAvailableSettings();
@ -804,64 +805,70 @@
</div>
</nav>
<div class="flex-1 overflow-y-auto scrollbar-none min-h-0 p-4 md:px-5">
{#if selectedTab === 'general'}
<General
{getModels}
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'interface'}
<Interface
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'connections'}
<Connections
saveSettings={async (updated: Record<string, any>) => {
await saveSettings(updated);
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'tools'}
<Integrations
saveSettings={async (updated: Record<string, any>) => {
await saveSettings(updated);
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'personalization'}
<Personalization
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'audio'}
<Audio
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'data_controls'}
<DataControls {saveSettings} />
{:else if selectedTab === 'archived_chats'}
<ArchivedChats />
{:else if selectedTab === 'account'}
<Account
{saveSettings}
saveHandler={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'about'}
<About />
{/if}
<div class="flex-1 min-h-0 p-4 md:px-5 flex flex-col">
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
{$i18n.t(selectedTabTitle)}
</h2>
<div class="flex-1 min-h-0 overflow-hidden">
{#if selectedTab === 'general'}
<General
{getModels}
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'interface'}
<Interface
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'connections'}
<Connections
saveSettings={async (updated: Record<string, any>) => {
await saveSettings(updated);
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'tools'}
<Integrations
saveSettings={async (updated: Record<string, any>) => {
await saveSettings(updated);
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'personalization'}
<Personalization
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'audio'}
<Audio
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'data_controls'}
<DataControls {saveSettings} />
{:else if selectedTab === 'archived_chats'}
<ArchivedChats />
{:else if selectedTab === 'account'}
<Account
{saveSettings}
saveHandler={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'about'}
<About />
{/if}
</div>
</div>
</Modal>