This commit is contained in:
Timothy Jaeryang Baek 2026-07-15 18:03:36 -04:00
parent ba067258de
commit 933ab1e1cd
25 changed files with 239 additions and 210 deletions

View file

@ -61,7 +61,7 @@
const inputClass =
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const selectClass =
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 pe-8 py-1.5 text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const verifyOllamaHandler = async () => {
// remove trailing slash from url
@ -384,7 +384,7 @@
<div class="flex-shrink-0 self-start">
<select
id="select-bearer-or-session"
class={`w-full text-sm pr-5 ${selectClass}`}
class={`w-full text-sm ${selectClass}`}
bind:value={auth_type}
>
<option value="none">{$i18n.t('None')}</option>

View file

@ -62,7 +62,7 @@
const inputClass =
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const selectClass =
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 pe-8 py-1.5 text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const stringifyJson = (value: object | null | undefined) => {
return JSON.stringify(value && Object.keys(value).length ? value : {}, null, 2);
@ -601,10 +601,7 @@
</div>
<div class="flex gap-2">
<div class="flex-shrink-0 self-start">
<select
class={`w-full text-sm pr-5 ${selectClass}`}
bind:value={policyStorage}
>
<select class={`w-full text-sm ${selectClass}`} bind:value={policyStorage}>
<option value="ephemeral">{$i18n.t('Ephemeral')}</option>
<option value="persistent">{$i18n.t('Persistent')}</option>
</select>
@ -816,7 +813,7 @@
<div class="flex gap-2">
<div class="flex-shrink-0 self-start">
<select class={`w-full text-sm pr-5 ${selectClass}`} bind:value={auth_type}>
<select class={`w-full text-sm ${selectClass}`} bind:value={auth_type}>
<option value="none">{$i18n.t('None')}</option>
<option value="bearer">{$i18n.t('Bearer')}</option>
{#if !direct}

View file

@ -72,7 +72,7 @@
const inputClass =
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const selectClass =
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
'rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 pe-8 py-1.5 text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const registerOAuthClientHandler = async () => {
if (url === '') {
@ -694,7 +694,7 @@
<div class="flex-shrink-0 self-start">
<select
id="select-bearer-or-session"
class={`w-full text-sm pr-5 ${selectClass}`}
class={`w-full text-sm ${selectClass}`}
bind:value={auth_type}
>
<option value="none">{$i18n.t('None')}</option>
@ -812,7 +812,7 @@
<div class="flex-shrink-0 self-start">
<select
id="select-bearer-or-session"
class={`w-full text-sm pr-5 ${selectClass}`}
class={`w-full text-sm ${selectClass}`}
bind:value={spec_type}
>
<option value="url">{$i18n.t('URL')}</option>
@ -894,7 +894,7 @@
<div class="flex flex-1 items-center">
<select
id="oauth-resource-parameter"
class={`w-full text-sm pr-5 ${selectClass}`}
class={`w-full text-sm ${selectClass}`}
bind:value={oauthResourceParameter}
>
<option value="auto">{$i18n.t('Automatic')}</option>

View file

@ -267,7 +267,7 @@
</div>
<div class="flex items-center relative">
<select
class="cursor-pointer w-fit pr-8 rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="cursor-pointer w-fit pr-8 rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={STT_ENGINE}
placeholder={$i18n.t('Select an engine')}
>
@ -285,7 +285,7 @@
<div>
<div class="mt-1 flex gap-2 mb-1">
<input
class="flex-1 w-full bg-transparent outline-hidden"
class="flex-1 w-full border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={STT_OPENAI_API_BASE_URL}
required
@ -301,7 +301,7 @@
</div>
<div class="flex items-center relative">
<select
class="cursor-pointer w-fit pr-8 rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="cursor-pointer w-fit pr-8 rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={STT_OPENAI_API_REQUEST_FORMAT}
>
<option value="multipart">{$i18n.t('Multipart Upload')}</option>
@ -434,7 +434,7 @@
<div>
<div class="mt-1 flex gap-2 mb-1">
<input
class="flex-1 w-full bg-transparent outline-hidden"
class="flex-1 w-full border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={STT_MISTRAL_API_BASE_URL}
required
@ -563,7 +563,7 @@
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 cursor-pointer rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 cursor-pointer rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={TTS_ENGINE}
placeholder={$i18n.t('Select a mode')}
on:change={async (e) => {
@ -597,7 +597,7 @@
<div>
<div class="mt-1 flex gap-2 mb-1">
<input
class="flex-1 w-full bg-transparent outline-hidden"
class="flex-1 w-full border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={TTS_OPENAI_API_BASE_URL}
required
@ -652,7 +652,7 @@
<div>
<div class="mt-1 flex gap-2 mb-1">
<input
class="flex-1 w-full bg-transparent outline-hidden"
class="flex-1 w-full border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={TTS_MISTRAL_API_BASE_URL}
required
@ -913,7 +913,7 @@
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 cursor-pointer rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 cursor-pointer rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
aria-label={$i18n.t('Select how to split message text for TTS requests')}
bind:value={TTS_SPLIT_ON}
>

View file

@ -50,7 +50,7 @@
const textareaClass =
'w-full rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const selectClass =
'cursor-pointer bg-transparent text-xs text-gray-600 outline-hidden dark:text-gray-400';
'h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 pe-8 text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const updateLdapServerHandler = async () => {
await updateLdapConfig(localStorage.token, ENABLE_LDAP);

View file

@ -25,7 +25,7 @@
const inlineInputClass =
'w-fit h-7 rounded-lg border border-transparent bg-transparent px-2 text-xs text-gray-600 outline-hidden transition-colors hover:bg-black/5 focus:border-blue-400 dark:text-gray-400 dark:hover:bg-white/5 dark:focus:border-blue-500';
const selectClass =
'cursor-pointer bg-transparent text-xs text-gray-600 outline-hidden dark:text-gray-400';
'h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 pe-8 text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const submitHandler = async () => {
const res = await setCodeExecutionConfig(localStorage.token, config);

View file

@ -387,7 +387,7 @@
</div>
<div class="">
<select
class="w-fit pr-8 rounded-lg px-2 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={RAGConfig.CONTENT_EXTRACTION_ENGINE}
>
<option value="">{$i18n.t('Default')}</option>
@ -429,7 +429,7 @@
</div>
<div class="">
<select
class="w-fit pr-8 rounded-lg px-2 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={RAGConfig.PDF_LOADER_MODE}
>
<option value="page">{$i18n.t('Page')}</option>
@ -448,7 +448,7 @@
className="w-full"
>
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter Datalab Marker API Base URL')}
bind:value={RAGConfig.DATALAB_MARKER_API_BASE_URL}
/>
@ -601,7 +601,7 @@
</div>
<div class="">
<select
class="w-fit pr-8 rounded-lg px-2 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={RAGConfig.DATALAB_MARKER_OUTPUT_FORMAT}
>
<option value="markdown">{$i18n.t('Markdown')}</option>
@ -614,7 +614,7 @@
<div class="my-0.5 flex flex-col gap-2 pr-2">
<div class="flex gap-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter External Document Loader URL')}
bind:value={RAGConfig.EXTERNAL_DOCUMENT_LOADER_URL}
/>
@ -688,7 +688,7 @@
<div class="flex w-full mt-1">
<div class="flex-1 mr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter Tika Server URL')}
bind:value={RAGConfig.TIKA_SERVER_URL}
/>
@ -697,7 +697,7 @@
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter Docling Server URL')}
bind:value={RAGConfig.DOCLING_SERVER_URL}
/>
@ -726,7 +726,7 @@
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter Document Intelligence Endpoint')}
bind:value={RAGConfig.DOCUMENT_INTELLIGENCE_ENDPOINT}
/>
@ -743,7 +743,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter Document Intelligence Model')}
bind:value={RAGConfig.DOCUMENT_INTELLIGENCE_MODEL}
/>
@ -753,7 +753,7 @@
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mistral_ocr'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter Mistral API Base URL')}
bind:value={RAGConfig.MISTRAL_OCR_API_BASE_URL}
/>
@ -780,7 +780,7 @@
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'paddleocr_vl'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter PaddleOCR-vl API Base URL')}
bind:value={RAGConfig.PADDLEOCR_VL_BASE_URL}
/>
@ -798,7 +798,7 @@
{$i18n.t('API Mode')}
</div>
<select
class="w-fit pr-8 rounded-lg px-2 text-xs bg-transparent outline-hidden"
class="w-fit pr-8 rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
bind:value={RAGConfig.MINERU_API_MODE}
on:change={() => {
// Auto-update URL when switching modes if it's empty or matches the opposite mode's default
@ -825,7 +825,7 @@
<!-- API URL -->
<div class="flex w-full mt-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={RAGConfig.MINERU_API_MODE === 'cloud'
? $i18n.t('https://mineru.net/api/v4')
: $i18n.t('http://localhost:8000')}
@ -846,7 +846,7 @@
{$i18n.t('API Timeout')}
</div>
<input
class="w-16 text-xs bg-transparent outline-hidden text-right"
class="w-16 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
type="number"
min="1"
bind:value={RAGConfig.MINERU_API_TIMEOUT}
@ -890,7 +890,7 @@
</Tooltip>
</div>
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('pdf, docx, pptx, xlsx')}
bind:value={RAGConfig.MINERU_FILE_EXTENSIONS}
/>
@ -926,7 +926,7 @@
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded-lg px-2 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={RAGConfig.TEXT_SPLITTER}
>
<option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
@ -1056,7 +1056,7 @@
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={RAG_EMBEDDING_ENGINE}
placeholder={$i18n.t('Select an embedding model engine')}
on:change={(e) => {
@ -1082,7 +1082,7 @@
{#if RAG_EMBEDDING_ENGINE === 'openai'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={OpenAIUrl}
required
@ -1097,7 +1097,7 @@
{:else if RAG_EMBEDDING_ENGINE === 'ollama'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={OllamaUrl}
required
@ -1113,7 +1113,7 @@
<div class="my-0.5 flex flex-col gap-2 pr-2 w-full">
<div class="flex gap-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={AzureOpenAIUrl}
required
@ -1122,7 +1122,7 @@
</div>
<div class="flex gap-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Version')}
bind:value={AzureOpenAIVersion}
required
@ -1142,7 +1142,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
bind:value={RAG_EMBEDDING_MODEL}
placeholder={$i18n.t('Set embedding model')}
required
@ -1153,7 +1153,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
model: RAG_EMBEDDING_MODEL.slice(-40)
})}
@ -1318,7 +1318,7 @@
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={RAGConfig.RAG_RERANKING_ENGINE}
placeholder={$i18n.t('Select a reranking model engine')}
on:change={(e) => {
@ -1338,7 +1338,7 @@
{#if RAGConfig.RAG_RERANKING_ENGINE === 'external'}
<div class="my-0.5 flex gap-2 pr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('API Base URL')}
bind:value={RAGConfig.RAG_EXTERNAL_RERANKER_URL}
required
@ -1362,7 +1362,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
model: 'BAAI/bge-reranker-v2-m3'
})}
@ -1397,7 +1397,7 @@
</div>
<div class="flex items-center relative">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="number"
placeholder={$i18n.t('Enter Top K')}
bind:value={RAGConfig.TOP_K}
@ -1414,7 +1414,7 @@
</div>
<div class="flex items-center relative">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="number"
placeholder={$i18n.t('Enter Top K Reranker')}
bind:value={RAGConfig.TOP_K_RERANKER}
@ -1433,7 +1433,7 @@
</div>
<div class="flex items-center relative">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="number"
step="0.01"
placeholder={$i18n.t('Enter Score')}
@ -1572,7 +1572,7 @@
placement="top-start"
>
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="text"
placeholder={$i18n.t('e.g. pdf, docx, txt')}
bind:value={RAGConfig.ALLOWED_FILE_EXTENSIONS}
@ -1594,7 +1594,7 @@
placement="top-start"
>
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="number"
placeholder={$i18n.t('Leave empty for unlimited')}
bind:value={RAGConfig.FILE_MAX_SIZE}
@ -1617,7 +1617,7 @@
placement="top-start"
>
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="number"
placeholder={$i18n.t('Leave empty for unlimited')}
bind:value={RAGConfig.FILE_MAX_COUNT}
@ -1640,7 +1640,7 @@
placement="top-start"
>
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="number"
placeholder={$i18n.t('Leave empty for no compression')}
bind:value={RAGConfig.FILE_IMAGE_COMPRESSION_WIDTH}
@ -1663,7 +1663,7 @@
placement="top-start"
>
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="number"
placeholder={$i18n.t('Leave empty for no compression')}
bind:value={RAGConfig.FILE_IMAGE_COMPRESSION_HEIGHT}

View file

@ -350,7 +350,7 @@
<div class="flex items-center">
<select
class="w-full py-1 text-sm rounded-lg bg-transparent {selectedModelId
class="w-full rounded-lg border border-gray-100/50 !bg-gray-50/40 px-2 py-1.5 text-sm text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500 {selectedModelId
? ''
: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
bind:value={selectedModelId}

View file

@ -455,7 +455,7 @@
{$i18n.t('Send events for')}
</div>
<select
class="text-xs bg-transparent outline-hidden text-gray-700 dark:text-gray-300"
class="rounded-lg border border-gray-100/50 !bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500"
bind:value={targetMode}
>
<option value="all">{$i18n.t('All users and system events')}</option>

View file

@ -460,7 +460,7 @@
<div class="flex flex-1 items-center">
<select
id="external-source-provider"
class="w-full text-sm bg-transparent outline-hidden"
class="w-full rounded-lg border border-gray-100/50 !bg-gray-50/40 px-2 py-1.5 text-sm text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500"
bind:value={sourceForm.provider}
on:change={updateProvider}
>

View file

@ -325,7 +325,7 @@
<Tooltip content={$i18n.t('Enter Model ID')} placement="top-start">
<input
list="model-list"
class=" text-right text-xs bg-transparent outline-hidden max-w-full w-52"
class=" text-right text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 max-w-full w-52"
bind:value={config.IMAGE_GENERATION_MODEL}
placeholder={$i18n.t('Select a model')}
required
@ -350,7 +350,7 @@
<Tooltip content={$i18n.t('Enter Image Size (e.g. 512x512)')} placement="top-start">
<input
class=" text-right text-xs bg-transparent outline-hidden max-w-full w-52"
class=" text-right text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 max-w-full w-52"
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
bind:value={config.IMAGE_SIZE}
/>
@ -372,7 +372,7 @@
placement="top-start"
>
<input
class=" text-right text-xs bg-transparent outline-hidden"
class=" text-right text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
bind:value={config.IMAGE_STEPS}
required
@ -404,7 +404,7 @@
</div>
<select
class="w-fit pr-8 cursor-pointer rounded-lg px-2 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 cursor-pointer rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={config.IMAGE_GENERATION_ENGINE}
placeholder={$i18n.t('Select Engine')}
>
@ -428,7 +428,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('API Base URL')}
bind:value={config.IMAGES_OPENAI_API_BASE_URL}
/>
@ -469,7 +469,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('API Version')}
bind:value={config.IMAGES_OPENAI_API_VERSION}
/>
@ -509,7 +509,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
bind:value={config.AUTOMATIC1111_BASE_URL}
/>
@ -623,7 +623,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
bind:value={config.COMFYUI_BASE_URL}
/>
@ -786,7 +786,7 @@
<div class="">
<Tooltip content={$i18n.t('Input Key (e.g. text, unet_name, steps)')}>
<input
class="py-1 w-24 text-xs bg-transparent outline-hidden"
class="py-1 w-24 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Key')}
bind:value={node.key}
required
@ -802,7 +802,7 @@
placement="top-start"
>
<input
class="w-full py-1 text-xs bg-transparent outline-hidden"
class="w-full py-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Node Ids')}
bind:value={node.node_ids}
/>
@ -830,7 +830,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('API Base URL')}
bind:value={config.IMAGES_GEMINI_API_BASE_URL}
/>
@ -869,7 +869,7 @@
</div>
<select
class="w-fit pr-8 cursor-pointer rounded-lg px-2 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 cursor-pointer rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={config.IMAGES_GEMINI_ENDPOINT_METHOD}
placeholder={$i18n.t('Select Method')}
>
@ -910,7 +910,7 @@
<Tooltip content={$i18n.t('Enter Model ID')} placement="top-start">
<input
list="model-list"
class="text-right text-xs bg-transparent outline-hidden max-w-full w-52"
class="text-right text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 max-w-full w-52"
bind:value={config.IMAGE_EDIT_MODEL}
placeholder={$i18n.t('Select a model')}
/>
@ -934,7 +934,7 @@
<Tooltip content={$i18n.t('Enter Image Size (e.g. 512x512)')} placement="top-start">
<input
class="text-right text-xs bg-transparent outline-hidden max-w-full w-52"
class="text-right text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 max-w-full w-52"
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
bind:value={config.IMAGE_EDIT_SIZE}
/>
@ -952,7 +952,7 @@
</div>
<select
class="w-fit pr-8 cursor-pointer rounded-lg px-2 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 cursor-pointer rounded-lg px-2 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={config.IMAGE_EDIT_ENGINE}
placeholder={$i18n.t('Select Engine')}
>
@ -975,7 +975,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('API Base URL')}
bind:value={config.IMAGES_EDIT_OPENAI_API_BASE_URL}
/>
@ -1016,7 +1016,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('API Version')}
bind:value={config.IMAGES_EDIT_OPENAI_API_VERSION}
/>
@ -1036,7 +1036,7 @@
<div class="flex w-full">
<div class="flex-1 mr-2">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
bind:value={config.IMAGES_EDIT_COMFYUI_BASE_URL}
/>
@ -1191,7 +1191,7 @@
<div class="">
<Tooltip content={$i18n.t('Input Key (e.g. text, unet_name, steps)')}>
<input
class="py-1 w-24 text-xs bg-transparent outline-hidden"
class="py-1 w-24 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Key')}
bind:value={node.key}
required
@ -1207,7 +1207,7 @@
placement="top-start"
>
<input
class="w-full py-1 text-xs bg-transparent outline-hidden"
class="w-full py-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Node Ids')}
bind:value={node.node_ids}
/>
@ -1235,7 +1235,7 @@
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full text-xs bg-transparent outline-hidden text-right"
class="w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
placeholder={$i18n.t('API Base URL')}
bind:value={config.IMAGES_EDIT_GEMINI_API_BASE_URL}
/>

View file

@ -60,7 +60,7 @@
<div class="flex flex-row flex-1 gap-2 items-start">
<select
class="w-fit capitalize rounded-xl text-xs bg-transparent outline-hidden pl-1 pr-5"
class="w-fit capitalize rounded-xl text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 pl-1 pr-5"
bind:value={banner.type}
required
>
@ -72,7 +72,7 @@
</select>
<Textarea
className="mr-2 text-xs w-full bg-transparent outline-hidden resize-none"
className="mr-2 text-xs w-full border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 resize-none"
placeholder={$i18n.t('Content')}
bind:value={banner.content}
maxSize={100}

View file

@ -12,7 +12,7 @@
{#if ollamaConfig}
<div class="flex-1 mb-2.5 pr-1.5 rounded-lg bg-gray-50 dark:text-gray-300 dark:bg-gray-850">
<select
class="w-full py-2 px-4 text-sm outline-hidden bg-transparent"
class="w-full py-2 px-4 text-sm outline-hidden border border-gray-100/50 !bg-gray-50/40 dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
bind:value={selectedUrlIdx}
placeholder={$i18n.t('Select an Ollama instance')}
>

View file

@ -812,7 +812,7 @@
class="flex-1 mr-2 pr-1.5 rounded-lg bg-gray-50 dark:text-gray-300 dark:bg-gray-850"
>
<select
class="w-full py-2 px-4 text-sm outline-hidden bg-transparent"
class="w-full py-2 px-4 text-sm outline-hidden border border-gray-100/50 !bg-gray-50/40 dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
bind:value={deleteModelTag}
placeholder={$i18n.t('Select a model')}
>

View file

@ -41,7 +41,7 @@
<div class="flex items-center -mr-1">
<select
class="w-full py-1 text-sm rounded-lg bg-transparent {selectedModelId
class="w-full py-1 text-sm rounded-lg border border-gray-100/50 !bg-gray-50/40 dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 {selectedModelId
? ''
: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden"
bind:value={selectedModelId}

View file

@ -43,7 +43,7 @@
const inputClass =
'w-full h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const selectClass =
'w-full h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
'w-full h-7 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 pe-8 text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const actionButtonClass =
'rounded-lg px-2 py-1 text-xs text-gray-500 transition-colors hover:bg-black/5 hover:text-gray-900 disabled:opacity-50 dark:text-gray-500 dark:hover:bg-white/5 dark:hover:text-white';
const iconButtonClass =

View file

@ -199,7 +199,7 @@
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={webConfig.WEB_SEARCH_ENGINE}
placeholder={$i18n.t('Select a engine')}
required
@ -1152,7 +1152,7 @@
</div>
<div class="flex items-center relative">
<select
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs bg-transparent outline-hidden text-right"
class="w-fit pr-8 rounded-lg px-2 p-1 text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 text-right"
bind:value={webConfig.WEB_LOADER_ENGINE}
placeholder={$i18n.t('Select a engine')}
>
@ -1171,7 +1171,7 @@
</div>
<div class="flex items-center relative">
<input
class="flex-1 w-full text-xs bg-transparent outline-hidden"
class="flex-1 w-full text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
placeholder={$i18n.t('Timeout')}
bind:value={webConfig.WEB_LOADER_TIMEOUT}
/>
@ -1386,7 +1386,7 @@
</div>
<div class="flex items-center relative">
<input
class="flex-1 w-full rounded-lg text-xs bg-transparent outline-hidden"
class="flex-1 w-full rounded-lg text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="text"
placeholder={$i18n.t('Enter language codes')}
bind:value={webConfig.YOUTUBE_LOADER_LANGUAGE}
@ -1401,7 +1401,7 @@
</div>
<div class="flex items-center relative">
<input
class="flex-1 w-full rounded-lg text-xs bg-transparent outline-hidden"
class="flex-1 w-full rounded-lg text-xs border border-gray-100/50 !bg-gray-50/40 outline-hidden dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300"
type="text"
placeholder={$i18n.t('Enter proxy URL (e.g. https://user:password@host:port)')}
bind:value={webConfig.YOUTUBE_LOADER_PROXY_URL}

View file

@ -17,6 +17,7 @@
import UserProfileImage from './Account/UserProfileImage.svelte';
import UserSettingField from './UserSettingField.svelte';
import UserSettingRow from './UserSettingRow.svelte';
import UserSettingSelect from './UserSettingSelect.svelte';
import UserSettingSection from './UserSettingSection.svelte';
const i18n = getContext('i18n');
@ -171,10 +172,10 @@
label={$i18n.t('Gender')}
description={$i18n.t('Choose the gender value stored on your profile.')}
>
<select
class={inputClass}
<UserSettingSelect
className="w-full"
bind:value={_gender}
aria-label={$i18n.t('Gender')}
ariaLabel={$i18n.t('Gender')}
on:change={(e) => {
console.log(_gender);
@ -190,7 +191,7 @@
<option value="male">{$i18n.t('Male')}</option>
<option value="female">{$i18n.t('Female')}</option>
<option value="custom">{$i18n.t('Custom')}</option>
</select>
</UserSettingSelect>
{#if _gender === 'custom'}
<input

View file

@ -10,6 +10,7 @@
import Tooltip from '$lib/components/common/Tooltip.svelte';
import UserSettingField from './UserSettingField.svelte';
import UserSettingRow from './UserSettingRow.svelte';
import UserSettingSelect from './UserSettingSelect.svelte';
import UserSettingSection from './UserSettingSection.svelte';
const dispatch = createEventDispatcher();
@ -40,8 +41,6 @@
let playbackRate = 1;
const inputClass =
'h-7 w-full rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const selectClass =
'h-7 w-fit min-w-28 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-right text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const actionButtonClass =
'text-xs text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-500 dark:hover:text-white';
@ -193,15 +192,14 @@
label={$i18n.t('Speech-to-Text Engine')}
description={$i18n.t('Choose the engine used to transcribe voice input.')}
>
<select
class={selectClass}
<UserSettingSelect
bind:value={STTEngine}
aria-label={$i18n.t('Speech-to-Text Engine')}
ariaLabel={$i18n.t('Speech-to-Text Engine')}
placeholder={$i18n.t('Select an engine')}
>
<option value="">{$i18n.t('Default')}</option>
<option value="web">{$i18n.t('Web API')}</option>
</select>
</UserSettingSelect>
</UserSettingRow>
<UserSettingRow
@ -252,15 +250,14 @@
label={$i18n.t('Text-to-Speech Engine')}
description={$i18n.t('Choose the engine used to read assistant responses aloud.')}
>
<select
class={selectClass}
<UserSettingSelect
bind:value={TTSEngine}
aria-label={$i18n.t('Text-to-Speech Engine')}
ariaLabel={$i18n.t('Text-to-Speech Engine')}
placeholder={$i18n.t('Select an engine')}
>
<option value="">{$i18n.t('Default')}</option>
<option value="browser-kokoro">{$i18n.t('Kokoro.js (Browser)')}</option>
</select>
</UserSettingSelect>
</UserSettingRow>
{#if TTSEngine === 'browser-kokoro'}
@ -268,10 +265,9 @@
label={$i18n.t('Kokoro.js Dtype')}
description={$i18n.t('Select the local model precision used by Kokoro.js.')}
>
<select
class={selectClass}
<UserSettingSelect
bind:value={TTSEngineConfig.dtype}
aria-label={$i18n.t('Kokoro.js Dtype')}
ariaLabel={$i18n.t('Kokoro.js Dtype')}
placeholder={$i18n.t('Select dtype')}
>
<option value="" disabled selected>{$i18n.t('Select dtype')}</option>
@ -279,7 +275,7 @@
<option value="fp16">fp16</option>
<option value="q8">q8</option>
<option value="q4">q4</option>
</select>
</UserSettingSelect>
</UserSettingRow>
{/if}
@ -364,7 +360,7 @@
label={$i18n.t('Set Voice')}
description={$i18n.t('Choose the browser voice used for speech output.')}
>
<select class={inputClass} bind:value={voice} aria-label={$i18n.t('Voice')}>
<UserSettingSelect className="w-full" bind:value={voice} ariaLabel={$i18n.t('Voice')}>
<option value="" selected={voice !== ''}>{$i18n.t('Default')}</option>
{#each voices.filter((v) => nonLocalVoices || v.localService === true) as _voice}
<option
@ -373,7 +369,7 @@
selected={voice === _voice.name}>{_voice.name}</option
>
{/each}
</select>
</UserSettingSelect>
</UserSettingField>
<UserSettingRow
label={$i18n.t('Allow non-local voices')}

View file

@ -13,6 +13,7 @@
import UserSettingField from './UserSettingField.svelte';
import UserSettingRow from './UserSettingRow.svelte';
import UserSettingSection from './UserSettingSection.svelte';
import UserSettingSelect from './UserSettingSelect.svelte';
export let saveSettings: Function;
export let getModels: Function;
@ -29,8 +30,6 @@
const systemPromptTextareaClass =
'w-full resize-y rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const selectClass =
'h-7 w-fit min-w-28 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-right text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500';
const actionButtonClass =
'text-xs text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-500 dark:hover:text-white';
@ -224,9 +223,9 @@
label={$i18n.t('Theme')}
description={$i18n.t('Choose the color theme used by the interface.')}
>
<select
class={selectClass}
<UserSettingSelect
bind:value={selectedTheme}
ariaLabel={$i18n.t('Theme')}
placeholder={$i18n.t('Select a theme')}
on:change={() => themeChangeHandler(selectedTheme)}
>
@ -237,16 +236,17 @@
{#if $config?.features?.enable_easter_eggs}
<option value="her">🌷 Her</option>
{/if}
</select>
</UserSettingSelect>
</UserSettingRow>
<UserSettingRow
label={$i18n.t('Language')}
description={$i18n.t('Choose the language used for interface text.')}
>
<select
class={selectClass}
<UserSettingSelect
bind:value={lang}
ariaLabel={$i18n.t('Language')}
className="w-48"
placeholder={$i18n.t('Select a language')}
on:change={(e) => {
changeLanguage(lang);
@ -255,7 +255,7 @@
{#each languages as language}
<option value={language['code']}>{language['title']}</option>
{/each}
</select>
</UserSettingSelect>
</UserSettingRow>
{#if $i18n.language === 'en-US' && !($config?.license_metadata ?? false)}
<div class="-mt-1 text-[0.6875rem] text-gray-400 dark:text-gray-600">

View file

@ -16,7 +16,9 @@
import UserSettingSection from './UserSettingSection.svelte';
import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
import Plus from '$lib/components/icons/Plus.svelte';
import Search from '$lib/components/icons/Search.svelte';
import Trash from '$lib/components/icons/Trash.svelte';
import XMark from '$lib/components/icons/XMark.svelte';
const dispatch = createEventDispatcher();
@ -34,8 +36,6 @@
let showClearConfirmDialog = false;
let showDeleteConfirm = false;
let query = '';
const inputClass =
'h-7 w-full rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-300 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-700 dark:focus:border-blue-500';
const actionButtonClass =
'shrink-0 text-xs text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-500 dark:hover:text-white';
@ -145,54 +145,13 @@
{#if enableMemory}
<div>
<div class="mb-2 flex items-center justify-between">
<div class="mb-1 flex items-center">
<div class="text-xs text-gray-600 dark:text-gray-400">
{$i18n.t('Saved Memories')}
{#if !loadingMemories}
<span class="ml-1 text-gray-400 dark:text-gray-600">{memories.length}</span>
{/if}
</div>
<Dropdown align="end">
<Tooltip content={$i18n.t('Actions')}>
<button
class="flex h-7 items-center gap-1.5 rounded-lg bg-transparent px-1.5 text-xs text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-500 dark:hover:text-white"
type="button"
>
<span>{$i18n.t('Actions')}</span>
<ChevronDown className="size-3" strokeWidth="2.5" />
</button>
</Tooltip>
<div slot="content">
<DropdownMenu className="w-[170px] shadow-sm">
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-lg bg-transparent px-2 text-xs hover:text-gray-900 disabled:cursor-default disabled:opacity-30 dark:hover:text-gray-100"
disabled={loadingMemories}
type="button"
on:click={() => {
selectedMemory = null;
showMemoryModal = true;
}}
>
<Plus className="size-3.5 shrink-0" strokeWidth="1.5" />
<div class="min-w-0 flex-1 truncate text-left">{$i18n.t('Add Memory')}</div>
</button>
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-lg bg-transparent px-2 text-xs hover:text-gray-900 disabled:cursor-default disabled:opacity-30 dark:hover:text-gray-100"
disabled={loadingMemories || memories.length === 0}
type="button"
on:click={() => {
showClearConfirmDialog = true;
}}
>
<Trash className="size-3.5 shrink-0" strokeWidth="1.5" />
<div class="min-w-0 flex-1 truncate text-left">{$i18n.t('Clear memory')}</div>
</button>
</DropdownMenu>
</div>
</Dropdown>
</div>
{#if loadingMemories}
@ -200,28 +159,76 @@
<Spinner className="size-4" />
</div>
{:else}
{#if memories.length > 0}
<div class="mb-2 flex items-center gap-2">
<input
class={inputClass}
bind:value={query}
placeholder={$i18n.t('Search Memories')}
maxlength="500"
/>
<div class="mb-2 flex min-w-0 items-center justify-between gap-3">
{#if memories.length > 0}
<div class="flex min-w-0 flex-1 items-center gap-2">
<Search className="size-3.5 shrink-0 text-gray-400 dark:text-gray-600" />
<input
data-settings-search
class="min-w-0 flex-1 bg-transparent py-0.5 text-xs text-gray-700 outline-hidden placeholder:text-gray-300 dark:text-gray-300 dark:placeholder:text-gray-700"
bind:value={query}
placeholder={$i18n.t('Search Memories')}
maxlength="500"
/>
{#if query}
<div class="shrink-0">
<button
class="rounded-lg p-0.5 text-gray-400 transition-colors hover:text-gray-700 dark:text-gray-600 dark:hover:text-gray-300"
type="button"
aria-label={$i18n.t('Clear search')}
on:click={() => {
query = '';
}}
>
<XMark className="size-3" strokeWidth="2" />
</button>
</div>
{/if}
</div>
{:else}
<div class="min-w-0 flex-1"></div>
{/if}
{#if query}
<Dropdown align="end">
<Tooltip content={$i18n.t('Actions')}>
<button
class="flex h-7 items-center gap-1.5 rounded-lg bg-transparent px-1.5 text-xs text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-500 dark:hover:text-white"
type="button"
class={actionButtonClass}
on:click={() => {
query = '';
}}
>
{$i18n.t('Clear')}
<span>{$i18n.t('Actions')}</span>
<ChevronDown className="size-3" strokeWidth="2.5" />
</button>
{/if}
</div>
{/if}
</Tooltip>
<div slot="content">
<DropdownMenu className="w-[170px] shadow-sm">
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-lg bg-transparent px-2 text-xs hover:text-gray-900 disabled:cursor-default disabled:opacity-30 dark:hover:text-gray-100"
type="button"
on:click={() => {
selectedMemory = null;
showMemoryModal = true;
}}
>
<Plus className="size-3.5 shrink-0" strokeWidth="1.5" />
<div class="min-w-0 flex-1 truncate text-left">{$i18n.t('Add Memory')}</div>
</button>
<button
class="flex h-[1.6875rem] w-full cursor-pointer select-none items-center gap-2 rounded-lg bg-transparent px-2 text-xs hover:text-gray-900 disabled:cursor-default disabled:opacity-30 dark:hover:text-gray-100"
disabled={memories.length === 0}
type="button"
on:click={() => {
showClearConfirmDialog = true;
}}
>
<Trash className="size-3.5 shrink-0" strokeWidth="1.5" />
<div class="min-w-0 flex-1 truncate text-left">{$i18n.t('Clear memory')}</div>
</button>
</DropdownMenu>
</div>
</Dropdown>
</div>
{#if sortedMemories.length === 0}
<div class="min-h-16 text-[0.6875rem] text-gray-400 dark:text-gray-600">

View file

@ -0,0 +1,19 @@
<script lang="ts">
export let value = '';
export let ariaLabel = '';
export let placeholder = '';
export let className = 'w-fit min-w-32';
export let selectClassName = '';
</script>
<div class="relative inline-flex {className}">
<select
bind:value
aria-label={ariaLabel || undefined}
title={placeholder || undefined}
class="h-7 w-full truncate rounded-lg border border-gray-100/50 !bg-gray-50/40 ps-2.5 pe-9 text-left text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:!bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500 {selectClassName}"
on:change
>
<slot />
</select>
</div>

View file

@ -308,7 +308,7 @@
<Modal size="lg" bind:show={showEditModal}>
<div class="px-4 pt-3 pb-4">
<div class="flex justify-between items-center mb-2 dark:text-gray-100">
<div class="text-sm font-medium">{$i18n.t('Edit Prompt')}</div>
<div class="text-xs">{$i18n.t('Edit Prompt')}</div>
<button
class="rounded-lg p-1 text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
aria-label={$i18n.t('Close')}
@ -326,7 +326,7 @@
<div class="mt-1">
<Textarea
className="text-sm w-full bg-transparent outline-hidden overflow-y-hidden resize-none"
className="text-xs w-full bg-transparent outline-hidden overflow-y-hidden resize-none"
placeholder={$i18n.t('Write a summary in 50 words that summarizes {{topic}}.')}
bind:value={content}
aria-label={$i18n.t('Prompt Content')}
@ -340,7 +340,7 @@
<div class="text-gray-500 text-xs">{$i18n.t('Commit Message')} ({$i18n.t('optional')})</div>
<div class="mt-1">
<input
class="text-sm w-full bg-transparent outline-hidden"
class="w-full bg-transparent text-xs outline-hidden"
placeholder={$i18n.t('Describe what changed...')}
aria-label={$i18n.t('Commit Message')}
bind:value={commitMessage}
@ -355,13 +355,13 @@
bind:checked={isProduction}
class="w-4 h-4 rounded border-gray-300 dark:border-gray-600"
/>
<span class="text-sm text-gray-700 dark:text-gray-300"
<span class="text-xs text-gray-700 dark:text-gray-300"
>{$i18n.t('Set as Production')}</span
>
</label>
<div>
<button
class="text-sm px-4 py-2 transition rounded-full {loading
class="px-3 py-1.5 text-xs transition rounded-full {loading
? 'cursor-not-allowed bg-gray-200 text-gray-500 dark:bg-gray-700 dark:text-gray-400'
: 'bg-black hover:bg-gray-900 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black'} flex justify-center"
type="submit"
@ -387,14 +387,14 @@
<div class="flex items-start justify-between gap-4 shrink-0">
<div class="min-w-0 flex-1">
<input
class="text-2xl w-full bg-transparent outline-hidden"
class="w-full bg-transparent text-sm outline-hidden"
placeholder={$i18n.t('Prompt Name')}
bind:value={name}
on:input={debouncedSaveMetadata}
{disabled}
/>
<div class="flex items-center gap-0.5 text-sm text-gray-500 w-full flex-1">
<div class="flex w-full flex-1 items-center gap-0.5 text-xs text-gray-500">
<span>/</span>
<input
class="bg-transparent outline-hidden"
@ -410,13 +410,13 @@
<div class="flex items-center gap-2 shrink-0 justify-end">
{#if !disabled}
<button
class="px-4 py-1 text-sm font-normal bg-black text-white dark:bg-white dark:text-black rounded-full hover:opacity-90 transition shadow-xs"
class="flex shrink-0 items-center rounded-lg bg-gray-50 px-2 py-1 text-xs text-gray-900 ring-1 ring-gray-200 transition hover:bg-gray-100 dark:bg-gray-850 dark:text-gray-100 dark:ring-gray-800 dark:hover:bg-gray-800"
on:click={() => (showEditModal = true)}
>
{$i18n.t('Edit')}
</button>
<AccessButton on:click={() => (showAccessControlModal = true)} />
<AccessButton on:click={() => (showAccessControlModal = true)} />
{:else}
<span class="text-xs text-gray-500 bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded-full"
>{$i18n.t('Read Only')}</span
@ -424,7 +424,7 @@
{/if}
</div>
<div class="mt-1.5">
<div>
<Tooltip content={$i18n.t('Click to copy ID')}>
<button
class="text-xs text-gray-500 font-mono px-2 py-1 rounded-lg cursor-pointer hover:underline transition"
@ -458,9 +458,9 @@
</div>
</div>
<div class="flex flex-col md:flex-row gap-4 flex-1 overflow-hidden pb-6">
<div class="flex flex-1 flex-col gap-3 overflow-hidden pb-4 md:flex-row">
<!-- Desktop History Sidebar -->
<div class="hidden md:flex md:flex-col w-72 shrink-0 overflow-hidden">
<div class="hidden w-64 shrink-0 overflow-hidden md:flex md:flex-col">
<div class="flex-1 overflow-y-auto">
{@render historySection()}
</div>
@ -474,9 +474,7 @@
{$i18n.t('Prompt Content')}
</div>
{#if selectedHistoryEntry}
<span
class="text-xs text-gray-500 font-mono bg-gray-100 dark:bg-gray-800 px-1.5 rounded"
>
<span class="px-1 font-mono text-xs text-gray-500">
{selectedHistoryEntry.id.slice(0, 7)}
</span>
{/if}
@ -519,9 +517,7 @@
</button>
</div>
<!-- Scrollable content -->
<div
class="bg-gray-50 dark:bg-gray-900 rounded-xl px-4 py-3 border border-gray-100/50 dark:border-gray-850/50 h-full overflow-y-auto"
>
<div class="h-full overflow-y-auto rounded-lg bg-transparent px-1 py-1">
<pre class="text-xs whitespace-pre-wrap font-mono pr-8">{selectedHistoryEntry?.snapshot
?.content || content}</pre>
</div>
@ -534,7 +530,7 @@
<div class="w-full max-h-full {modal ? 'h-full flex flex-col' : ''}">
{#if modal}
<div class="flex justify-between items-center dark:text-gray-100 px-5 pt-4 pb-2">
<h3 class="text-base font-normal">{$i18n.t('Create Prompt')}</h3>
<h3 class="text-sm">{$i18n.t('Create Prompt')}</h3>
<button
class="self-center shrink-0 ml-2"
aria-label={$i18n.t('Close')}
@ -560,7 +556,7 @@
<div class="flex flex-col w-full">
<div class="flex items-center">
<input
class="{modal ? 'text-base' : 'text-2xl'} w-full bg-transparent outline-hidden"
class="w-full bg-transparent text-sm outline-hidden"
placeholder={$i18n.t('Name')}
bind:value={name}
required
@ -568,7 +564,7 @@
<div class="self-center shrink-0">
<AccessButton on:click={() => (showAccessControlModal = true)} />
</div>
</div>
</div>
<div class="flex gap-0.5 items-center text-xs text-gray-500">
<div>/</div>
<input
@ -601,14 +597,14 @@
<div class={modal ? 'mt-1 flex-1 min-h-0 flex flex-col' : 'mt-1'}>
{#if modal}
<textarea
class="text-sm w-full flex-1 min-h-0 bg-transparent outline-hidden resize-none"
class="w-full flex-1 min-h-0 resize-none bg-transparent text-xs outline-hidden"
placeholder={$i18n.t('Write a summary in 50 words that summarizes {{topic}}.')}
bind:value={content}
required
></textarea>
{:else}
<Textarea
className="text-sm w-full bg-transparent outline-hidden overflow-y-hidden resize-none"
className="text-xs w-full bg-transparent outline-hidden overflow-y-hidden resize-none"
placeholder={$i18n.t('Write a summary in 50 words that summarizes {{topic}}.')}
bind:value={content}
rows={6}
@ -640,8 +636,8 @@
<button
class="{modal
? 'px-3.5 py-1.5 text-sm rounded-full w-fit'
: 'text-sm w-full lg:w-fit px-4 py-2 rounded-xl'} transition bg-black hover:bg-gray-900 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black flex justify-center"
? 'px-3.5 py-1.5 text-xs rounded-full w-fit'
: 'text-xs w-full lg:w-fit px-4 py-2 rounded-xl'} transition bg-black hover:bg-gray-900 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black flex justify-center"
type="submit"
disabled={loading}
>
@ -669,10 +665,10 @@
<div class="flex">
<!-- Content -->
<button
class="flex-1 text-left px-3.5 py-2 mb-1 rounded-2xl transition group
class="mb-0.5 flex-1 rounded-lg px-2 py-1.5 text-left transition group
{selectedHistoryEntry?.id === entry.id
? 'bg-gray-100/50 dark:bg-gray-850/50'
: 'hover:bg-gray-100/50 dark:hover:bg-gray-850/50'}"
? 'bg-gray-50/60 dark:bg-white/[0.03]'
: 'hover:bg-gray-50/60 dark:hover:bg-white/[0.03]'}"
on:click={() => (selectedHistoryEntry = entry)}
>
<!-- Commit Message -->

View file

@ -100,5 +100,11 @@
</script>
{#key model}
<ModelEditor {model} {onSubmit} />
<ModelEditor
{model}
{onSubmit}
onBack={async () => {
await goto('/workspace/models');
}}
/>
{/key}

View file

@ -52,5 +52,12 @@
</script>
{#if model}
<ModelEditor edit={true} {model} {onSubmit} />
<ModelEditor
edit={true}
{model}
{onSubmit}
onBack={async () => {
await goto('/workspace/models');
}}
/>
{/if}