feat: improve onboarding model selection (#416)
Merge validating & validated status into `Next` button; disable `Next` when the provider is invalid. Co-authored-by: Neko <neko@ayaka.moe>
This commit is contained in:
@@ -17,8 +17,6 @@ dialogs:
|
||||
baseUrl: Base URL
|
||||
baseUrlHelp: API endpoint URL (use default if unsure)
|
||||
accountId: Account ID
|
||||
validating: Validating configuration...
|
||||
validationSuccess: First configuration validated successful.
|
||||
validationFailed: Configuration validation failed
|
||||
validationError: 'Validation error: {error}'
|
||||
skipForNow: Skip for now
|
||||
@@ -26,7 +24,9 @@ dialogs:
|
||||
next: Next
|
||||
start: Let's do it!
|
||||
select-model: Choose model
|
||||
language:
|
||||
no-models: No available models
|
||||
no-models-help: Please return to the previous step and check your API key, or check the network connection.
|
||||
language:
|
||||
title: Language
|
||||
description: >
|
||||
Change the language of the AIRI interface. This will not affect the language
|
||||
|
||||
@@ -17,7 +17,6 @@ dialogs:
|
||||
baseUrl: URL Base
|
||||
baseUrlHelp: URL del endpoint de la API (usa el predeterminado si no estás seguro)
|
||||
accountId: ID de Cuenta
|
||||
validating: Validando configuración...
|
||||
validationFailed: La validación de la configuración falló
|
||||
validationError: 'Error de validación: {error}'
|
||||
skipForNow: Omitir por ahora
|
||||
|
||||
@@ -15,8 +15,6 @@ dialogs:
|
||||
baseUrl: 基础 URL
|
||||
baseUrlHelp: API 端点 URL(如果不确定请使用默认值)
|
||||
accountId: 账户 ID
|
||||
validating: 正在验证配置...
|
||||
validationSuccess: 配置初步验证通过。
|
||||
validationFailed: 配置验证失败
|
||||
validationError: 验证错误:{error}
|
||||
skipForNow: 暂时跳过
|
||||
@@ -24,6 +22,8 @@ dialogs:
|
||||
next: 下一步
|
||||
start: 开始吧!
|
||||
select-model: 选择模型
|
||||
no-models: 找不到可用模型
|
||||
no-models-help: 请返回上一步并检查您的 API Key,或检查网络连接。
|
||||
language:
|
||||
title: 语言
|
||||
description: 切换显示界面的语言
|
||||
|
||||
@@ -140,7 +140,6 @@ async function validateConfiguration() {
|
||||
isValidating.value++
|
||||
// service startup time
|
||||
const startValidationTimestamp = performance.now()
|
||||
validationMessage.value = t('settings.dialogs.onboarding.validating')
|
||||
let finalValidationMessage = ''
|
||||
|
||||
try {
|
||||
@@ -160,10 +159,7 @@ async function validateConfiguration() {
|
||||
|
||||
isValid.value = validationResult.valid
|
||||
|
||||
if (isValid.value) {
|
||||
finalValidationMessage = t('settings.dialogs.onboarding.validationSuccess')
|
||||
}
|
||||
else {
|
||||
if (!isValid.value) {
|
||||
finalValidationMessage = validationResult.reason
|
||||
}
|
||||
}
|
||||
@@ -177,7 +173,7 @@ async function validateConfiguration() {
|
||||
setTimeout(() => {
|
||||
isValidating.value--
|
||||
validationMessage.value = finalValidationMessage
|
||||
}, Math.max(0, debounceTime - (performance.now() - startValidationTimestamp)))
|
||||
}, debounceTime - (performance.now() - startValidationTimestamp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,31 +409,13 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
</Alert>
|
||||
<div v-if="(isValid || isValidating > 0) && validationMessage" class="mt-4">
|
||||
<div
|
||||
class="flex items-center rounded-lg p-3" :class="[
|
||||
(isValidating > 0)
|
||||
? 'bg-blue-50 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300'
|
||||
: 'bg-green-50 text-green-700 dark:bg-green-900/30 dark:text-green-300',
|
||||
]"
|
||||
>
|
||||
<div
|
||||
class="mr-2 text-lg" :class="[
|
||||
(isValidating > 0)
|
||||
? 'i-svg-spinners:3-dots-fade'
|
||||
: 'i-solar:check-circle-bold-duotone',
|
||||
]"
|
||||
/>
|
||||
{{ validationMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<Button
|
||||
:label="t('settings.dialogs.onboarding.next')"
|
||||
:disabled="!selectedProviderId || (needsApiKey && apiKey.trim().length === 0)"
|
||||
:loading="isLoadingActiveProviderModels"
|
||||
:loading="isLoadingActiveProviderModels || isValidating > 0"
|
||||
:disabled="!selectedProviderId || (needsApiKey && apiKey.trim().length === 0) || !isValid"
|
||||
@click="handleFinishProviderConfiguration"
|
||||
/>
|
||||
</div>
|
||||
@@ -459,9 +437,10 @@ onMounted(() => {
|
||||
<!-- Using the new RadioCardManySelect component -->
|
||||
<div flex-1>
|
||||
<RadioCardManySelect
|
||||
v-if="providerModels.length > 0"
|
||||
v-model="activeModel"
|
||||
v-model:search-query="modelSearchQuery"
|
||||
:items="providerModels.sort((a, b) => a.id === activeModel ? -1 : b.id === activeModel ? 1 : 0)"
|
||||
:items="providerModels.toSorted((a, b) => a.id === activeModel ? -1 : b.id === activeModel ? 1 : 0)"
|
||||
:searchable="true"
|
||||
:search-placeholder="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.search_placeholder')"
|
||||
:search-no-results-title="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.no_search_results')"
|
||||
@@ -472,6 +451,17 @@ onMounted(() => {
|
||||
:collapse-button-text="t('settings.pages.modules.consciousness.sections.section.provider-model-selection.collapse')"
|
||||
list-class="max-h-[calc(100dvh-17rem)] sm:max-h-120"
|
||||
/>
|
||||
|
||||
<Alert v-else type="error">
|
||||
<template #title>
|
||||
{{ t('settings.dialogs.onboarding.no-models') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="whitespace-pre-wrap break-all">
|
||||
{{ t('settings.dialogs.onboarding.no-models-help') }}
|
||||
</div>
|
||||
</template>
|
||||
</Alert>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
|
||||
Reference in New Issue
Block a user