style(ConfigManager): make sections collapsible
This commit is contained in:
@ -21,6 +21,19 @@
|
|||||||
/** If loading AI models failed, use a plain input to improve UX */
|
/** If loading AI models failed, use a plain input to improve UX */
|
||||||
const isLoadAiModelsFailed = ref(false)
|
const isLoadAiModelsFailed = ref(false)
|
||||||
|
|
||||||
|
const activeSections = ref(['0', '1'])
|
||||||
|
const settingSections = computed(() => [
|
||||||
|
{
|
||||||
|
label: t('settings.ai.provider'),
|
||||||
|
icon: 'i-lucide-bot',
|
||||||
|
slot: 'ai',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('settings.webSearch.provider'),
|
||||||
|
icon: 'i-lucide-search',
|
||||||
|
slot: 'web-search',
|
||||||
|
},
|
||||||
|
])
|
||||||
const aiProviderOptions = computed(() => [
|
const aiProviderOptions = computed(() => [
|
||||||
{
|
{
|
||||||
label: t('settings.ai.providers.openaiCompatible.title'),
|
label: t('settings.ai.providers.openaiCompatible.title'),
|
||||||
@ -170,183 +183,198 @@
|
|||||||
<UButton icon="i-lucide-settings" />
|
<UButton icon="i-lucide-settings" />
|
||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="flex flex-col gap-y-2">
|
<UAccordion
|
||||||
|
v-model="activeSections"
|
||||||
|
type="multiple"
|
||||||
|
:items="settingSections"
|
||||||
|
collapsible
|
||||||
|
>
|
||||||
<!-- AI provider -->
|
<!-- AI provider -->
|
||||||
<h3 class="font-bold">{{ $t('settings.ai.provider') }}</h3>
|
<template #ai>
|
||||||
<UFormField>
|
<div class="flex flex-col gap-y-2 mb-2">
|
||||||
<template v-if="selectedAiProvider?.help" #help>
|
<UFormField>
|
||||||
<i18n-t
|
<template v-if="selectedAiProvider?.help" #help>
|
||||||
class="whitespace-pre-wrap"
|
<i18n-t
|
||||||
:keypath="selectedAiProvider.help"
|
class="whitespace-pre-wrap"
|
||||||
tag="span"
|
:keypath="selectedAiProvider.help"
|
||||||
|
tag="span"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
v-if="selectedAiProvider.link"
|
||||||
|
class="!p-0"
|
||||||
|
:to="selectedAiProvider.link"
|
||||||
|
target="_blank"
|
||||||
|
variant="link"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
selectedAiProvider.linkText || selectedAiProvider.link
|
||||||
|
}}
|
||||||
|
</UButton>
|
||||||
|
</i18n-t>
|
||||||
|
</template>
|
||||||
|
<USelect
|
||||||
|
v-model="config.ai.provider"
|
||||||
|
class="w-full"
|
||||||
|
:items="aiProviderOptions"
|
||||||
|
/>
|
||||||
|
</UFormField>
|
||||||
|
<UFormField
|
||||||
|
:label="$t('settings.ai.apiKey')"
|
||||||
|
:required="config.ai.provider !== 'ollama'"
|
||||||
>
|
>
|
||||||
<UButton
|
<PasswordInput
|
||||||
v-if="selectedAiProvider.link"
|
v-model="config.ai.apiKey"
|
||||||
class="!p-0"
|
class="w-full"
|
||||||
:to="selectedAiProvider.link"
|
:placeholder="$t('settings.ai.apiKey')"
|
||||||
target="_blank"
|
/>
|
||||||
variant="link"
|
</UFormField>
|
||||||
>
|
<UFormField :label="$t('settings.ai.apiBase')">
|
||||||
{{ selectedAiProvider.linkText || selectedAiProvider.link }}
|
<UInput
|
||||||
</UButton>
|
v-model="config.ai.apiBase"
|
||||||
</i18n-t>
|
class="w-full"
|
||||||
</template>
|
:placeholder="aiApiBase"
|
||||||
<USelect
|
/>
|
||||||
v-model="config.ai.provider"
|
</UFormField>
|
||||||
class="w-full"
|
<UFormField :label="$t('settings.ai.model')" required>
|
||||||
:items="aiProviderOptions"
|
<UInputMenu
|
||||||
/>
|
v-if="aiModelOptions.length && !isLoadAiModelsFailed"
|
||||||
</UFormField>
|
v-model="config.ai.model"
|
||||||
|
class="w-full"
|
||||||
<div class="flex flex-col gap-y-2">
|
:items="aiModelOptions"
|
||||||
<UFormField
|
:placeholder="$t('settings.ai.model')"
|
||||||
:label="$t('settings.ai.apiKey')"
|
:loading="loadingAiModels"
|
||||||
:required="config.ai.provider !== 'ollama'"
|
create-item
|
||||||
>
|
@create="createAndSelectAiModel"
|
||||||
<PasswordInput
|
/>
|
||||||
v-model="config.ai.apiKey"
|
<UInput
|
||||||
class="w-full"
|
v-else
|
||||||
:placeholder="$t('settings.ai.apiKey')"
|
v-model="config.ai.model"
|
||||||
/>
|
class="w-full"
|
||||||
</UFormField>
|
:placeholder="$t('settings.ai.model')"
|
||||||
<UFormField :label="$t('settings.ai.apiBase')">
|
/>
|
||||||
<UInput
|
</UFormField>
|
||||||
v-model="config.ai.apiBase"
|
<UFormField :label="$t('settings.ai.contextSize')">
|
||||||
class="w-full"
|
<template #help>
|
||||||
:placeholder="aiApiBase"
|
{{ $t('settings.ai.contextSizeHelp') }}
|
||||||
/>
|
</template>
|
||||||
</UFormField>
|
<UInput
|
||||||
<UFormField :label="$t('settings.ai.model')" required>
|
v-model="config.ai.contextSize"
|
||||||
<UInputMenu
|
class="w-26"
|
||||||
v-if="aiModelOptions.length && !isLoadAiModelsFailed"
|
type="number"
|
||||||
v-model="config.ai.model"
|
placeholder="128000"
|
||||||
class="w-full"
|
:min="512"
|
||||||
:items="aiModelOptions"
|
/>
|
||||||
:placeholder="$t('settings.ai.model')"
|
tokens
|
||||||
:loading="loadingAiModels"
|
</UFormField>
|
||||||
create-item
|
</div>
|
||||||
@create="createAndSelectAiModel"
|
</template>
|
||||||
/>
|
|
||||||
<UInput
|
|
||||||
v-else
|
|
||||||
v-model="config.ai.model"
|
|
||||||
class="w-full"
|
|
||||||
:placeholder="$t('settings.ai.model')"
|
|
||||||
/>
|
|
||||||
</UFormField>
|
|
||||||
<UFormField :label="$t('settings.ai.contextSize')">
|
|
||||||
<template #help>
|
|
||||||
{{ $t('settings.ai.contextSizeHelp') }}
|
|
||||||
</template>
|
|
||||||
<UInput
|
|
||||||
v-model="config.ai.contextSize"
|
|
||||||
class="w-26"
|
|
||||||
type="number"
|
|
||||||
placeholder="128000"
|
|
||||||
:min="512"
|
|
||||||
/>
|
|
||||||
tokens
|
|
||||||
</UFormField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<USeparator class="my-2" />
|
|
||||||
|
|
||||||
<!-- Web search -->
|
<!-- Web search -->
|
||||||
<h3 class="font-bold"> {{ $t('settings.webSearch.provider') }} </h3>
|
<template #web-search>
|
||||||
<UFormField>
|
<div class="flex flex-col gap-y-2">
|
||||||
<template #help>
|
<UFormField>
|
||||||
<i18n-t
|
<template #help>
|
||||||
v-if="selectedWebSearchProvider?.help"
|
<i18n-t
|
||||||
:keypath="selectedWebSearchProvider.help"
|
v-if="selectedWebSearchProvider?.help"
|
||||||
tag="p"
|
:keypath="selectedWebSearchProvider.help"
|
||||||
|
tag="p"
|
||||||
|
>
|
||||||
|
<UButton
|
||||||
|
class="!p-0"
|
||||||
|
:to="selectedWebSearchProvider.link"
|
||||||
|
target="_blank"
|
||||||
|
variant="link"
|
||||||
|
>
|
||||||
|
{{ selectedWebSearchProvider.link }}
|
||||||
|
</UButton>
|
||||||
|
</i18n-t>
|
||||||
|
</template>
|
||||||
|
<USelect
|
||||||
|
v-model="config.webSearch.provider"
|
||||||
|
class="w-30"
|
||||||
|
:items="webSearchProviderOptions"
|
||||||
|
/>
|
||||||
|
</UFormField>
|
||||||
|
<UFormField
|
||||||
|
:label="$t('settings.webSearch.apiKey')"
|
||||||
|
:required="!config.webSearch.apiBase"
|
||||||
>
|
>
|
||||||
<UButton
|
<PasswordInput
|
||||||
class="!p-0"
|
v-model="config.webSearch.apiKey"
|
||||||
:to="selectedWebSearchProvider.link"
|
class="w-full"
|
||||||
target="_blank"
|
:placeholder="$t('settings.webSearch.apiKey')"
|
||||||
variant="link"
|
/>
|
||||||
>
|
</UFormField>
|
||||||
{{ selectedWebSearchProvider.link }}
|
<UFormField
|
||||||
</UButton>
|
v-if="selectedWebSearchProvider?.supportsCustomApiBase"
|
||||||
</i18n-t>
|
:label="$t('settings.webSearch.apiBase')"
|
||||||
</template>
|
>
|
||||||
<USelect
|
<UInput
|
||||||
v-model="config.webSearch.provider"
|
v-model="config.webSearch.apiBase"
|
||||||
class="w-30"
|
class="w-full"
|
||||||
:items="webSearchProviderOptions"
|
:placeholder="webSearchApiBase"
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
<UFormField
|
<UFormField :label="$t('settings.webSearch.queryLanguage')">
|
||||||
:label="$t('settings.webSearch.apiKey')"
|
<template #help>
|
||||||
:required="!config.webSearch.apiBase"
|
<i18n-t
|
||||||
>
|
class="whitespace-pre-wrap"
|
||||||
<PasswordInput
|
keypath="settings.webSearch.queryLanguageHelp"
|
||||||
v-model="config.webSearch.apiKey"
|
tag="p"
|
||||||
class="w-full"
|
/>
|
||||||
:placeholder="$t('settings.webSearch.apiKey')"
|
</template>
|
||||||
/>
|
<LangSwitcher
|
||||||
</UFormField>
|
:value="config.webSearch.searchLanguage"
|
||||||
<UFormField
|
@update="config.webSearch.searchLanguage = $event"
|
||||||
v-if="selectedWebSearchProvider?.supportsCustomApiBase"
|
private
|
||||||
:label="$t('settings.webSearch.apiBase')"
|
/>
|
||||||
>
|
</UFormField>
|
||||||
<UInput
|
<UFormField :label="$t('settings.webSearch.concurrencyLimit')">
|
||||||
v-model="config.webSearch.apiBase"
|
<template #help>
|
||||||
class="w-full"
|
{{ $t('settings.webSearch.concurrencyLimitHelp') }}
|
||||||
:placeholder="webSearchApiBase"
|
</template>
|
||||||
/>
|
<UInput
|
||||||
</UFormField>
|
v-model="config.webSearch.concurrencyLimit"
|
||||||
<UFormField :label="$t('settings.webSearch.queryLanguage')">
|
class="w-15"
|
||||||
<template #help>
|
type="number"
|
||||||
<i18n-t
|
placeholder="2"
|
||||||
class="whitespace-pre-wrap"
|
:min="1"
|
||||||
keypath="settings.webSearch.queryLanguageHelp"
|
:max="5"
|
||||||
tag="p"
|
:step="1"
|
||||||
/>
|
/>
|
||||||
</template>
|
</UFormField>
|
||||||
<LangSwitcher
|
|
||||||
:value="config.webSearch.searchLanguage"
|
|
||||||
@update="config.webSearch.searchLanguage = $event"
|
|
||||||
private
|
|
||||||
/>
|
|
||||||
</UFormField>
|
|
||||||
<UFormField :label="$t('settings.webSearch.concurrencyLimit')">
|
|
||||||
<template #help>
|
|
||||||
{{ $t('settings.webSearch.concurrencyLimitHelp') }}
|
|
||||||
</template>
|
|
||||||
<UInput
|
|
||||||
v-model="config.webSearch.concurrencyLimit"
|
|
||||||
class="w-15"
|
|
||||||
type="number"
|
|
||||||
placeholder="2"
|
|
||||||
:min="1"
|
|
||||||
:max="5"
|
|
||||||
:step="1"
|
|
||||||
/>
|
|
||||||
</UFormField>
|
|
||||||
|
|
||||||
<!-- Tavily-specific settings -->
|
<!-- Tavily-specific settings -->
|
||||||
<template v-if="config.webSearch.provider === 'tavily'">
|
<template v-if="config.webSearch.provider === 'tavily'">
|
||||||
<UFormField
|
<UFormField
|
||||||
:label="$t('settings.webSearch.providers.tavily.advancedSearch')"
|
:label="
|
||||||
:help="$t('settings.webSearch.providers.tavily.advancedSearchHelp')"
|
$t('settings.webSearch.providers.tavily.advancedSearch')
|
||||||
>
|
"
|
||||||
<USwitch v-model="config.webSearch.tavilyAdvancedSearch" />
|
:help="
|
||||||
</UFormField>
|
$t('settings.webSearch.providers.tavily.advancedSearchHelp')
|
||||||
<UFormField
|
"
|
||||||
:label="$t('settings.webSearch.providers.tavily.searchTopic')"
|
>
|
||||||
:help="$t('settings.webSearch.providers.tavily.searchTopicHelp')"
|
<USwitch v-model="config.webSearch.tavilyAdvancedSearch" />
|
||||||
>
|
</UFormField>
|
||||||
<USelect
|
<UFormField
|
||||||
v-model="config.webSearch.tavilySearchTopic"
|
:label="$t('settings.webSearch.providers.tavily.searchTopic')"
|
||||||
class="w-30"
|
:help="
|
||||||
:items="tavilySearchTopicOptions"
|
$t('settings.webSearch.providers.tavily.searchTopicHelp')
|
||||||
placeholder="general"
|
"
|
||||||
/>
|
>
|
||||||
</UFormField>
|
<USelect
|
||||||
|
v-model="config.webSearch.tavilySearchTopic"
|
||||||
|
class="w-30"
|
||||||
|
:items="tavilySearchTopicOptions"
|
||||||
|
placeholder="general"
|
||||||
|
/>
|
||||||
|
</UFormField>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</UAccordion>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex items-center justify-between gap-2 w-full">
|
<div class="flex items-center justify-between gap-2 w-full">
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
|
Reference in New Issue
Block a user