mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
refactor(web): align route paths, page dirs and i18n keys with sidebar labels
- Rename route paths to match sidebar tab labels:
models→providers, search-providers→web-search,
memory-providers→memory, tts-providers→speech,
email-providers→email, browser-contexts→browser,
settings(profile)→profile
- Rename page directories accordingly
- Rename i18n keys: sidebar.models→providers, searchProvider→webSearch,
memoryProvider→memory, ttsProvider→speech, emailProvider→email,
browserContext→browser
- Fix bot detail tab value 'settings' → 'general' to match label
- Fix ZH bots.tabs.general untranslated ("General" → "通用")
- Align usage page title with sidebar label
This commit is contained in:
@@ -96,33 +96,33 @@ const navItems = computed(() => [
|
||||
icon: ['fas', 'robot'],
|
||||
},
|
||||
{
|
||||
title: t('sidebar.models'),
|
||||
name: 'models',
|
||||
title: t('sidebar.providers'),
|
||||
name: 'providers',
|
||||
icon: ['fas', 'cubes'],
|
||||
},
|
||||
{
|
||||
title: t('sidebar.searchProvider'),
|
||||
name: 'search-providers',
|
||||
title: t('sidebar.webSearch'),
|
||||
name: 'web-search',
|
||||
icon: ['fas', 'globe'],
|
||||
},
|
||||
{
|
||||
title: t('sidebar.memoryProvider'),
|
||||
name: 'memory-providers',
|
||||
title: t('sidebar.memory'),
|
||||
name: 'memory',
|
||||
icon: ['fas', 'brain'],
|
||||
},
|
||||
{
|
||||
title: t('sidebar.ttsProvider'),
|
||||
name: 'tts-providers',
|
||||
title: t('sidebar.speech'),
|
||||
name: 'speech',
|
||||
icon: ['fas', 'volume-high'],
|
||||
},
|
||||
{
|
||||
title: t('sidebar.emailProvider'),
|
||||
name: 'email-providers',
|
||||
title: t('sidebar.email'),
|
||||
name: 'email',
|
||||
icon: ['fas', 'envelope'],
|
||||
},
|
||||
{
|
||||
title: t('sidebar.browserContexts'),
|
||||
name: 'browser-contexts',
|
||||
title: t('sidebar.browser'),
|
||||
name: 'browser',
|
||||
icon: ['fas', 'window-maximize'],
|
||||
},
|
||||
{
|
||||
@@ -132,7 +132,7 @@ const navItems = computed(() => [
|
||||
},
|
||||
{
|
||||
title: t('sidebar.settings'),
|
||||
name: 'settings',
|
||||
name: 'profile',
|
||||
icon: ['fas', 'gear'],
|
||||
},
|
||||
])
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<SidebarMenuButton
|
||||
:tooltip="displayTitle"
|
||||
class="h-10 px-2.5"
|
||||
@click="router.push({ name: 'settings' })"
|
||||
@click="router.push({ name: 'profile' })"
|
||||
>
|
||||
<div class="size-9 shrink-0 rounded-full border border-border bg-accent overflow-hidden p-[1.385px]">
|
||||
<img
|
||||
|
||||
@@ -58,17 +58,17 @@
|
||||
"session": "Session",
|
||||
"chat": "Chat",
|
||||
"bots": "Bots",
|
||||
"models": "Providers",
|
||||
"searchProvider": "Web Search",
|
||||
"memoryProvider": "Memory",
|
||||
"ttsProvider": "Speech",
|
||||
"emailProvider": "Email",
|
||||
"providers": "Providers",
|
||||
"webSearch": "Web Search",
|
||||
"memory": "Memory",
|
||||
"speech": "Speech",
|
||||
"email": "Email",
|
||||
"settings": "Settings",
|
||||
"home": "Home",
|
||||
"mcp": "MCP",
|
||||
"platform": "Platform",
|
||||
"usage": "Usage",
|
||||
"browserContexts": "Browser"
|
||||
"browser": "Browser"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"main": "Home"
|
||||
@@ -179,7 +179,7 @@
|
||||
"searchSessionPlaceholder": "Search"
|
||||
},
|
||||
"models": {
|
||||
"title": "Providers",
|
||||
"title": "Models",
|
||||
"searchPlaceholder": "Search providers…",
|
||||
"addModel": "Add Model",
|
||||
"editModel": "Edit Model",
|
||||
@@ -260,7 +260,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"searchProvider": {
|
||||
"webSearch": {
|
||||
"title": "Web Search",
|
||||
"add": "Add Web Search",
|
||||
"empty": "No search providers",
|
||||
@@ -289,7 +289,7 @@
|
||||
"yandex": "Yandex"
|
||||
}
|
||||
},
|
||||
"memoryProvider": {
|
||||
"memory": {
|
||||
"title": "Memory Providers",
|
||||
"add": "Add Memory Provider",
|
||||
"empty": "No memory providers",
|
||||
@@ -337,7 +337,7 @@
|
||||
"openviking": "OpenViking"
|
||||
}
|
||||
},
|
||||
"ttsProvider": {
|
||||
"speech": {
|
||||
"title": "Speech",
|
||||
"add": "Add Speech Provider",
|
||||
"providerType": "Provider Type",
|
||||
@@ -373,7 +373,7 @@
|
||||
"failed": "Synthesis failed"
|
||||
}
|
||||
},
|
||||
"emailProvider": {
|
||||
"email": {
|
||||
"title": "Email",
|
||||
"add": "Add Email",
|
||||
"providerType": "Provider Type",
|
||||
@@ -416,7 +416,7 @@
|
||||
"logoutFailed": "Failed to revoke authorization"
|
||||
}
|
||||
},
|
||||
"browserContext": {
|
||||
"browser": {
|
||||
"title": "Browser Contexts",
|
||||
"add": "Add Browser Context",
|
||||
"searchPlaceholder": "Search browser contexts...",
|
||||
@@ -1088,7 +1088,7 @@
|
||||
}
|
||||
},
|
||||
"usage": {
|
||||
"title": "Token Usage",
|
||||
"title": "Usage",
|
||||
"selectBot": "Select Bot",
|
||||
"selectBotPlaceholder": "Choose a bot to view usage",
|
||||
"timeRange": "Time Range",
|
||||
|
||||
@@ -58,17 +58,17 @@
|
||||
"session":"会话",
|
||||
"chat": "对话",
|
||||
"bots": "Bots",
|
||||
"models": "模型管理",
|
||||
"searchProvider": "搜索提供方",
|
||||
"memoryProvider": "记忆",
|
||||
"ttsProvider": "语音合成",
|
||||
"emailProvider": "邮件提供方",
|
||||
"providers": "模型管理",
|
||||
"webSearch": "搜索提供方",
|
||||
"memory": "记忆",
|
||||
"speech": "语音合成",
|
||||
"email": "邮件提供方",
|
||||
"settings": "设置",
|
||||
"home": "首页",
|
||||
"mcp": "MCP",
|
||||
"platform": "接入平台",
|
||||
"usage": "用量统计",
|
||||
"browserContexts": "浏览器"
|
||||
"browser": "浏览器"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"main": "主页"
|
||||
@@ -256,7 +256,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"searchProvider": {
|
||||
"webSearch": {
|
||||
"title": "搜索提供方",
|
||||
"add": "添加搜索提供方",
|
||||
"empty": "暂无搜索提供方",
|
||||
@@ -285,7 +285,7 @@
|
||||
"yandex": "Yandex"
|
||||
}
|
||||
},
|
||||
"memoryProvider": {
|
||||
"memory": {
|
||||
"title": "记忆提供方",
|
||||
"add": "添加记忆提供方",
|
||||
"empty": "暂无记忆提供方",
|
||||
@@ -333,7 +333,7 @@
|
||||
"openviking": "OpenViking"
|
||||
}
|
||||
},
|
||||
"ttsProvider": {
|
||||
"speech": {
|
||||
"title": "语音合成",
|
||||
"add": "添加语音合成提供方",
|
||||
"providerType": "提供方类型",
|
||||
@@ -369,7 +369,7 @@
|
||||
"failed": "合成失败"
|
||||
}
|
||||
},
|
||||
"emailProvider": {
|
||||
"email": {
|
||||
"title": "邮件提供方",
|
||||
"add": "添加邮件提供方",
|
||||
"providerType": "提供方类型",
|
||||
@@ -412,7 +412,7 @@
|
||||
"logoutFailed": "撤销授权失败"
|
||||
}
|
||||
},
|
||||
"browserContext": {
|
||||
"browser": {
|
||||
"title": "浏览器上下文",
|
||||
"add": "添加浏览器上下文",
|
||||
"searchPlaceholder": "搜索浏览器上下文...",
|
||||
@@ -598,7 +598,7 @@
|
||||
},
|
||||
"tabs": {
|
||||
"overview": "概览",
|
||||
"general": "General",
|
||||
"general": "通用",
|
||||
"memory": "记忆",
|
||||
"channels": "平台",
|
||||
"container": "容器",
|
||||
@@ -1084,7 +1084,7 @@
|
||||
}
|
||||
},
|
||||
"usage": {
|
||||
"title": "Token 用量",
|
||||
"title": "用量统计",
|
||||
"selectBot": "选择 Bot",
|
||||
"selectBotPlaceholder": "选择一个 Bot 查看用量",
|
||||
"timeRange": "时间范围",
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
class="rounded-md border border-border bg-card px-3 py-2 text-xs text-muted-foreground"
|
||||
>
|
||||
{{ $t('bots.settings.memoryModePreview', {
|
||||
mode: $t(`memoryProvider.modeNames.${selectedBuiltinMemoryMode}`),
|
||||
mode: $t(`memory.modeNames.${selectedBuiltinMemoryMode}`),
|
||||
}) }}
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
:options="options"
|
||||
:placeholder="placeholder || ''"
|
||||
:aria-label="placeholder || 'Select browser context'"
|
||||
:search-placeholder="$t('browserContext.searchPlaceholder')"
|
||||
:search-placeholder="$t('browser.searchPlaceholder')"
|
||||
search-aria-label="Search browser contexts"
|
||||
:empty-text="$t('browserContext.emptyTitle')"
|
||||
:empty-text="$t('browser.emptyTitle')"
|
||||
:show-group-headers="false"
|
||||
>
|
||||
<template #trigger="{ open, displayLabel }">
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
:options="options"
|
||||
:placeholder="placeholder || ''"
|
||||
:aria-label="placeholder || 'Select memory provider'"
|
||||
:search-placeholder="$t('memoryProvider.searchPlaceholder')"
|
||||
:search-placeholder="$t('memory.searchPlaceholder')"
|
||||
search-aria-label="Search memory providers"
|
||||
:empty-text="$t('memoryProvider.empty')"
|
||||
:empty-text="$t('memory.empty')"
|
||||
:show-group-headers="false"
|
||||
>
|
||||
<template #trigger="{ open, displayLabel }">
|
||||
@@ -83,7 +83,7 @@ const options = computed<SearchableSelectOption[]>(() => {
|
||||
value: provider.id || '',
|
||||
label: provider.name || provider.id || '',
|
||||
description: provider.provider === 'builtin'
|
||||
? t(`memoryProvider.modeNames.${provider.config?.memory_mode || 'off'}`)
|
||||
? t(`memory.modeNames.${provider.config?.memory_mode || 'off'}`)
|
||||
: provider.provider,
|
||||
keywords: [
|
||||
provider.name ?? '',
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
:options="options"
|
||||
:placeholder="placeholder || ''"
|
||||
:aria-label="placeholder || 'Select search provider'"
|
||||
:search-placeholder="$t('searchProvider.searchPlaceholder')"
|
||||
:search-placeholder="$t('webSearch.searchPlaceholder')"
|
||||
search-aria-label="Search providers"
|
||||
:empty-text="$t('searchProvider.empty')"
|
||||
:empty-text="$t('webSearch.empty')"
|
||||
:show-group-headers="false"
|
||||
>
|
||||
<template #trigger="{ open, displayLabel }">
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
:options="options"
|
||||
:placeholder="placeholder || ''"
|
||||
:aria-label="placeholder || 'Select TTS model'"
|
||||
:search-placeholder="$t('ttsProvider.searchPlaceholder')"
|
||||
:search-placeholder="$t('speech.searchPlaceholder')"
|
||||
search-aria-label="Search TTS models"
|
||||
:empty-text="$t('ttsProvider.emptyTitle')"
|
||||
:empty-text="$t('speech.emptyTitle')"
|
||||
>
|
||||
<template #trigger="{ open, displayLabel }">
|
||||
<Button
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
:options="options"
|
||||
:placeholder="placeholder || ''"
|
||||
:aria-label="placeholder || 'Select TTS provider'"
|
||||
:search-placeholder="$t('ttsProvider.searchPlaceholder')"
|
||||
:search-placeholder="$t('speech.searchPlaceholder')"
|
||||
search-aria-label="Search TTS providers"
|
||||
:empty-text="$t('ttsProvider.emptyTitle')"
|
||||
:empty-text="$t('speech.emptyTitle')"
|
||||
:show-group-headers="false"
|
||||
>
|
||||
<template #trigger="{ open, displayLabel }">
|
||||
|
||||
@@ -276,7 +276,7 @@ const tabList = computed(() => {
|
||||
{
|
||||
value: 'overview', label: 'bots.tabs.overview', component: BotOverview, params: {}
|
||||
},
|
||||
{ value: 'settings', label: 'bots.tabs.general', component: BotSettings, params: { 'bot-id': bot_id, 'bot-type': bot.value?.type } },
|
||||
{ value: 'general', label: 'bots.tabs.general', component: BotSettings, params: { 'bot-id': bot_id, 'bot-type': bot.value?.type } },
|
||||
{ value: 'container', label: 'bots.tabs.container', component: BotContainer, params: {} },
|
||||
{ value: 'memory', label: 'bots.tabs.memory', component: BotMemory, params: { 'bot-id': bot_id } },
|
||||
{ value: 'channels', label: 'bots.tabs.channels', component: BotChannels, params: { 'bot-id': bot_id } },
|
||||
|
||||
+5
-5
@@ -2,9 +2,9 @@
|
||||
<section>
|
||||
<FormDialogShell
|
||||
v-model:open="open"
|
||||
:title="$t('browserContext.add')"
|
||||
:title="$t('browser.add')"
|
||||
:cancel-text="$t('common.cancel')"
|
||||
:submit-text="$t('browserContext.add')"
|
||||
:submit-text="$t('browser.add')"
|
||||
:submit-disabled="(form.meta.value.valid === false) || isLoading"
|
||||
:loading="isLoading"
|
||||
@submit="handleCreate"
|
||||
@@ -17,7 +17,7 @@
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('browserContext.add') }}
|
||||
/> {{ $t('browser.add') }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #body>
|
||||
@@ -28,13 +28,13 @@
|
||||
>
|
||||
<FormItem>
|
||||
<Label :for="componentField.id || 'browser-context-name'">
|
||||
{{ $t('browserContext.name') }}
|
||||
{{ $t('browser.name') }}
|
||||
</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
:id="componentField.id || 'browser-context-name'"
|
||||
type="text"
|
||||
:placeholder="$t('browserContext.namePlaceholder')"
|
||||
:placeholder="$t('browser.namePlaceholder')"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
+20
-20
@@ -8,7 +8,7 @@
|
||||
/>
|
||||
<div>
|
||||
<h2 class="text-sm font-semibold">
|
||||
{{ curContext?.name || $t('browserContext.title') }}
|
||||
{{ curContext?.name || $t('browser.title') }}
|
||||
</h2>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ curContext?.id }}
|
||||
@@ -25,11 +25,11 @@
|
||||
name="name"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.name') }}</Label>
|
||||
<Label>{{ $t('browser.name') }}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="text"
|
||||
:placeholder="$t('browserContext.namePlaceholder')"
|
||||
:placeholder="$t('browser.namePlaceholder')"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
<Separator class="my-4" />
|
||||
<h3 class="text-xs font-medium text-foreground">
|
||||
{{ $t('browserContext.config') }}
|
||||
{{ $t('browser.config') }}
|
||||
</h3>
|
||||
|
||||
<FormField
|
||||
@@ -46,7 +46,7 @@
|
||||
name="core"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.core') }}</Label>
|
||||
<Label>{{ $t('browser.core') }}</Label>
|
||||
<FormControl>
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
@@ -59,7 +59,7 @@
|
||||
: 'border-border bg-card text-muted-foreground hover:bg-accent'"
|
||||
@click="handleChange(c)"
|
||||
>
|
||||
{{ $t(`browserContext.${c}`) }}
|
||||
{{ $t(`browser.${c}`) }}
|
||||
</button>
|
||||
</div>
|
||||
</FormControl>
|
||||
@@ -72,7 +72,7 @@
|
||||
name="viewportWidth"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.viewportWidth') }}</Label>
|
||||
<Label>{{ $t('browser.viewportWidth') }}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
@@ -87,7 +87,7 @@
|
||||
name="viewportHeight"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.viewportHeight') }}</Label>
|
||||
<Label>{{ $t('browser.viewportHeight') }}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
@@ -103,11 +103,11 @@
|
||||
name="userAgent"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.userAgent') }}</Label>
|
||||
<Label>{{ $t('browser.userAgent') }}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="text"
|
||||
:placeholder="$t('browserContext.userAgentPlaceholder')"
|
||||
:placeholder="$t('browser.userAgentPlaceholder')"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -120,7 +120,7 @@
|
||||
name="deviceScaleFactor"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.deviceScaleFactor') }}</Label>
|
||||
<Label>{{ $t('browser.deviceScaleFactor') }}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
@@ -136,11 +136,11 @@
|
||||
name="locale"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.locale') }}</Label>
|
||||
<Label>{{ $t('browser.locale') }}</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="text"
|
||||
:placeholder="$t('browserContext.localePlaceholder')"
|
||||
:placeholder="$t('browser.localePlaceholder')"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -153,11 +153,11 @@
|
||||
name="timezoneId"
|
||||
>
|
||||
<FormItem>
|
||||
<Label>{{ $t('browserContext.timezoneId') }}</Label>
|
||||
<Label>{{ $t('browser.timezoneId') }}</Label>
|
||||
<FormControl>
|
||||
<TimezoneSelect
|
||||
:model-value="value || emptyTimezoneValue"
|
||||
:placeholder="$t('browserContext.timezonePlaceholder')"
|
||||
:placeholder="$t('browser.timezonePlaceholder')"
|
||||
allow-empty
|
||||
:empty-label="$t('common.optional')"
|
||||
@update:model-value="(val) => handleChange(val === emptyTimezoneValue ? '' : val)"
|
||||
@@ -178,7 +178,7 @@
|
||||
@update:model-value="handleChange"
|
||||
/>
|
||||
</FormControl>
|
||||
<Label class="mt-0!">{{ $t('browserContext.isMobile') }}</Label>
|
||||
<Label class="mt-0!">{{ $t('browser.isMobile') }}</Label>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
@update:model-value="handleChange"
|
||||
/>
|
||||
</FormControl>
|
||||
<Label class="mt-0!">{{ $t('browserContext.ignoreHTTPSErrors') }}</Label>
|
||||
<Label class="mt-0!">{{ $t('browser.ignoreHTTPSErrors') }}</Label>
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</div>
|
||||
@@ -203,7 +203,7 @@
|
||||
|
||||
<div class="flex gap-2 items-center justify-between">
|
||||
<ConfirmPopover
|
||||
:title="$t('browserContext.deleteConfirm')"
|
||||
:title="$t('browser.deleteConfirm')"
|
||||
:confirm-text="$t('common.delete')"
|
||||
@confirm="handleDelete"
|
||||
>
|
||||
@@ -363,7 +363,7 @@ const handleSave = form.handleSubmit(async (values) => {
|
||||
() => updateMutation({ id, name: values.name, config }),
|
||||
{
|
||||
fallbackMessage: t('common.saveFailed'),
|
||||
onSuccess: () => toast.success(t('browserContext.saveSuccess')),
|
||||
onSuccess: () => toast.success(t('browser.saveSuccess')),
|
||||
},
|
||||
)
|
||||
})
|
||||
@@ -373,7 +373,7 @@ async function handleDelete() {
|
||||
if (!id) return
|
||||
try {
|
||||
await deleteMutation(id)
|
||||
toast.success(t('browserContext.deleteSuccess'))
|
||||
toast.success(t('browser.deleteSuccess'))
|
||||
} catch (err) {
|
||||
toast.error(resolveApiErrorMessage(err, t('common.deleteFailed')))
|
||||
}
|
||||
+3
-3
@@ -108,8 +108,8 @@ const openStatus = reactive({
|
||||
<FontAwesomeIcon :icon="['fas', 'window-maximize']" />
|
||||
</EmptyMedia>
|
||||
</EmptyHeader>
|
||||
<EmptyTitle>{{ $t('browserContext.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('browserContext.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyTitle>{{ $t('browser.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('browser.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyContent>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -118,7 +118,7 @@ const openStatus = reactive({
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('browserContext.add') }}
|
||||
/> {{ $t('browser.add') }}
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
+4
-4
@@ -2,9 +2,9 @@
|
||||
<section>
|
||||
<FormDialogShell
|
||||
v-model:open="open"
|
||||
:title="$t('emailProvider.add')"
|
||||
:title="$t('email.add')"
|
||||
:cancel-text="$t('common.cancel')"
|
||||
:submit-text="$t('emailProvider.add')"
|
||||
:submit-text="$t('email.add')"
|
||||
:submit-disabled="(form.meta.value.valid === false) || isLoading"
|
||||
:loading="isLoading"
|
||||
@submit="handleCreate"
|
||||
@@ -17,7 +17,7 @@
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('emailProvider.add') }}
|
||||
/> {{ $t('email.add') }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #body>
|
||||
@@ -46,7 +46,7 @@
|
||||
>
|
||||
<FormItem>
|
||||
<Label :for="componentField.id || 'email-provider-type'">
|
||||
{{ $t('emailProvider.providerType') }}
|
||||
{{ $t('email.providerType') }}
|
||||
</Label>
|
||||
<FormControl>
|
||||
<Select v-bind="componentField">
|
||||
+16
-16
@@ -50,7 +50,7 @@
|
||||
class="space-y-2"
|
||||
>
|
||||
<Label :for="field.type === 'bool' || field.type === 'enum' ? undefined : `email-field-${field.key}`">
|
||||
{{ $te(`emailProvider.fields.${field.key}`) ? $t(`emailProvider.fields.${field.key}`) : (field.title || field.key) }}
|
||||
{{ $te(`email.fields.${field.key}`) ? $t(`email.fields.${field.key}`) : (field.title || field.key) }}
|
||||
<span
|
||||
v-if="!field.required"
|
||||
class="text-xs text-muted-foreground ml-1"
|
||||
@@ -136,29 +136,29 @@
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div class="flex-1 min-w-[220px]">
|
||||
<p class="text-xs font-medium">
|
||||
{{ $t('emailProvider.oauth.title') }}
|
||||
{{ $t('email.oauth.title') }}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground mt-0.5">
|
||||
{{ $t('emailProvider.oauth.description') }}
|
||||
{{ $t('email.oauth.description') }}
|
||||
</p>
|
||||
<p
|
||||
class="text-xs mt-2"
|
||||
:class="oauthTokenExpired ? 'text-destructive' : 'text-muted-foreground'"
|
||||
>
|
||||
<template v-if="oauthStatusLoading">
|
||||
{{ $t('emailProvider.oauth.status.checking') }}
|
||||
{{ $t('email.oauth.status.checking') }}
|
||||
</template>
|
||||
<template v-else-if="oauthStatus && !oauthStatus.configured">
|
||||
{{ $t('emailProvider.oauth.status.notConfigured') }}
|
||||
{{ $t('email.oauth.status.notConfigured') }}
|
||||
</template>
|
||||
<template v-else-if="oauthTokenExpired">
|
||||
{{ $t('emailProvider.oauth.status.expired') }}
|
||||
{{ $t('email.oauth.status.expired') }}
|
||||
</template>
|
||||
<template v-else-if="oauthStatus && oauthStatus.has_token">
|
||||
{{ oauthStatus.email_address ? $t('emailProvider.oauth.status.authorized', { email: oauthStatus.email_address }) : $t('emailProvider.oauth.status.authorizedUnknown') }}
|
||||
{{ oauthStatus.email_address ? $t('email.oauth.status.authorized', { email: oauthStatus.email_address }) : $t('email.oauth.status.authorizedUnknown') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('emailProvider.oauth.status.missing') }}
|
||||
{{ $t('email.oauth.status.missing') }}
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
@@ -174,7 +174,7 @@
|
||||
:icon="['fas', 'key']"
|
||||
class="mr-1.5"
|
||||
/>
|
||||
{{ $t('emailProvider.oauth.authorize') }}
|
||||
{{ $t('email.oauth.authorize') }}
|
||||
</LoadingButton>
|
||||
<LoadingButton
|
||||
v-if="hasOAuthToken"
|
||||
@@ -183,7 +183,7 @@
|
||||
:loading="revokeLoading"
|
||||
@click="handleRevoke"
|
||||
>
|
||||
{{ $t('emailProvider.oauth.logout') }}
|
||||
{{ $t('email.oauth.logout') }}
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</div>
|
||||
@@ -191,7 +191,7 @@
|
||||
|
||||
<section class="flex justify-end mt-6 gap-4">
|
||||
<ConfirmPopover
|
||||
:message="$t('emailProvider.deleteConfirm')"
|
||||
:message="$t('email.deleteConfirm')"
|
||||
:loading="deleteLoading"
|
||||
@confirm="handleDelete"
|
||||
>
|
||||
@@ -378,12 +378,12 @@ async function handleAuthorize() {
|
||||
path: { id: curProviderId.value },
|
||||
})
|
||||
if (error || !data?.auth_url) {
|
||||
throw new Error(t('emailProvider.oauth.authorizeFailed'))
|
||||
throw new Error(t('email.oauth.authorizeFailed'))
|
||||
}
|
||||
window.open(data.auth_url, '_blank', 'noopener,noreferrer')
|
||||
toast.success(t('emailProvider.oauth.authorizeOpened'))
|
||||
toast.success(t('email.oauth.authorizeOpened'))
|
||||
} catch (e: unknown) {
|
||||
toast.error(e instanceof Error ? e.message : t('emailProvider.oauth.authorizeFailed'))
|
||||
toast.error(e instanceof Error ? e.message : t('email.oauth.authorizeFailed'))
|
||||
} finally {
|
||||
authorizeLoading.value = false
|
||||
}
|
||||
@@ -419,10 +419,10 @@ async function handleRevoke() {
|
||||
path: { id: curProviderId.value },
|
||||
})
|
||||
if (error) throw error
|
||||
toast.success(t('emailProvider.oauth.logoutSuccess'))
|
||||
toast.success(t('email.oauth.logoutSuccess'))
|
||||
await fetchOAuthStatus()
|
||||
} catch (error: unknown) {
|
||||
toast.error(error instanceof Error ? error.message : t('emailProvider.oauth.logoutFailed'))
|
||||
toast.error(error instanceof Error ? error.message : t('email.oauth.logoutFailed'))
|
||||
} finally {
|
||||
revokeLoading.value = false
|
||||
}
|
||||
@@ -103,8 +103,8 @@ const openStatus = reactive({ addOpen: false })
|
||||
<FontAwesomeIcon :icon="['fas', 'envelope']" />
|
||||
</EmptyMedia>
|
||||
</EmptyHeader>
|
||||
<EmptyTitle>{{ $t('emailProvider.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('emailProvider.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyTitle>{{ $t('email.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('email.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyContent>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -113,7 +113,7 @@ const openStatus = reactive({ addOpen: false })
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('emailProvider.add') }}
|
||||
/> {{ $t('email.add') }}
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
+9
-9
@@ -9,23 +9,23 @@
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-2"
|
||||
/>
|
||||
{{ $t('memoryProvider.add') }}
|
||||
{{ $t('memory.add') }}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ $t('memoryProvider.add') }}</DialogTitle>
|
||||
<DialogTitle>{{ $t('memory.add') }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div class="space-y-4 py-4">
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('memoryProvider.name') }}</Label>
|
||||
<Label>{{ $t('memory.name') }}</Label>
|
||||
<Input
|
||||
v-model="form.name"
|
||||
:placeholder="$t('memoryProvider.namePlaceholder')"
|
||||
:placeholder="$t('memory.namePlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('memoryProvider.provider') }}</Label>
|
||||
<Label>{{ $t('memory.provider') }}</Label>
|
||||
<Select v-model:model-value="form.provider">
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
@@ -33,13 +33,13 @@
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="builtin">
|
||||
{{ $t('memoryProvider.providerNames.builtin') }}
|
||||
{{ $t('memory.providerNames.builtin') }}
|
||||
</SelectItem>
|
||||
<SelectItem value="mem0">
|
||||
{{ $t('memoryProvider.providerNames.mem0') }}
|
||||
{{ $t('memory.providerNames.mem0') }}
|
||||
</SelectItem>
|
||||
<SelectItem value="openviking">
|
||||
{{ $t('memoryProvider.providerNames.openviking') }}
|
||||
{{ $t('memory.providerNames.openviking') }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
@@ -115,7 +115,7 @@ async function handleCreate() {
|
||||
},
|
||||
throwOnError: true,
|
||||
})
|
||||
toast.success(t('memoryProvider.saveSuccess'))
|
||||
toast.success(t('memory.saveSuccess'))
|
||||
queryCache.invalidateQueries({ key: ['memory-providers'] })
|
||||
open.value = false
|
||||
form.name = ''
|
||||
+26
-26
@@ -9,11 +9,11 @@
|
||||
{{ curProvider.name }}
|
||||
</h3>
|
||||
<p class="text-xs text-muted-foreground mt-0.5">
|
||||
{{ $t(`memoryProvider.providerNames.${curProvider.provider}`, curProvider.provider) }}
|
||||
{{ $t(`memory.providerNames.${curProvider.provider}`, curProvider.provider) }}
|
||||
</p>
|
||||
</div>
|
||||
<ConfirmPopover
|
||||
:message="$t('memoryProvider.deleteConfirm')"
|
||||
:message="$t('memory.deleteConfirm')"
|
||||
@confirm="handleDelete"
|
||||
>
|
||||
<template #trigger>
|
||||
@@ -36,19 +36,19 @@
|
||||
|
||||
<!-- Name -->
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('memoryProvider.name') }}</Label>
|
||||
<Label>{{ $t('memory.name') }}</Label>
|
||||
<Input
|
||||
v-model="form.name"
|
||||
:placeholder="$t('memoryProvider.namePlaceholder')"
|
||||
:placeholder="$t('memory.namePlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Builtin Config (model selectors) -->
|
||||
<template v-if="curProvider.provider === 'builtin'">
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('memoryProvider.builtinMode') }}</Label>
|
||||
<Label>{{ $t('memory.builtinMode') }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.builtinModeDescription') }}
|
||||
{{ $t('memory.builtinModeDescription') }}
|
||||
</p>
|
||||
<div class="inline-flex rounded-xl border border-border bg-muted/70 p-1">
|
||||
<div class="relative grid grid-cols-3">
|
||||
@@ -62,7 +62,7 @@
|
||||
:class="builtinModeButtonClass('off')"
|
||||
@click="handleBuiltinModeChange('off')"
|
||||
>
|
||||
{{ $t('memoryProvider.modeNames.off') }}
|
||||
{{ $t('memory.modeNames.off') }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -70,7 +70,7 @@
|
||||
:class="builtinModeButtonClass('sparse')"
|
||||
@click="handleBuiltinModeChange('sparse')"
|
||||
>
|
||||
{{ $t('memoryProvider.modeNames.sparse') }}
|
||||
{{ $t('memory.modeNames.sparse') }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -78,7 +78,7 @@
|
||||
:class="builtinModeButtonClass('dense')"
|
||||
@click="handleBuiltinModeChange('dense')"
|
||||
>
|
||||
{{ $t('memoryProvider.modeNames.dense') }}
|
||||
{{ $t('memory.modeNames.dense') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -89,10 +89,10 @@
|
||||
class="rounded-lg border border-border bg-card p-4 space-y-2"
|
||||
>
|
||||
<h4 class="text-xs font-medium">
|
||||
{{ $t('memoryProvider.modeNames.off') }}
|
||||
{{ $t('memory.modeNames.off') }}
|
||||
</h4>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.modeDescriptions.off') }}
|
||||
{{ $t('memory.modeDescriptions.off') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -102,15 +102,15 @@
|
||||
>
|
||||
<div class="space-y-1">
|
||||
<h4 class="text-xs font-medium">
|
||||
{{ $t('memoryProvider.sparseSectionTitle') }}
|
||||
{{ $t('memory.sparseSectionTitle') }}
|
||||
</h4>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.modeDescriptions.sparse') }}
|
||||
{{ $t('memory.modeDescriptions.sparse') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border border-border bg-background px-3 py-2 text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.sparseInstallHint') }}
|
||||
{{ $t('memory.sparseInstallHint') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -120,29 +120,29 @@
|
||||
>
|
||||
<div class="space-y-1">
|
||||
<h4 class="text-xs font-medium">
|
||||
{{ $t('memoryProvider.denseSectionTitle') }}
|
||||
{{ $t('memory.denseSectionTitle') }}
|
||||
</h4>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.modeDescriptions.dense') }}
|
||||
{{ $t('memory.modeDescriptions.dense') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>{{ $t('memoryProvider.denseEmbeddingModel') }}</Label>
|
||||
<Label>{{ $t('memory.denseEmbeddingModel') }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.denseEmbeddingModelDescription') }}
|
||||
{{ $t('memory.denseEmbeddingModelDescription') }}
|
||||
</p>
|
||||
<ModelSelect
|
||||
v-model="configForm.embedding_model_id"
|
||||
:models="models"
|
||||
:providers="providers"
|
||||
model-type="embedding"
|
||||
:placeholder="$t('memoryProvider.denseEmbeddingModel')"
|
||||
:placeholder="$t('memory.denseEmbeddingModel')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border border-border bg-background px-3 py-2 text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.denseQdrantHint') }}
|
||||
{{ $t('memory.denseQdrantHint') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -163,17 +163,17 @@
|
||||
class="text-xs"
|
||||
:class="collection.qdrant?.ok ? 'text-foreground' : 'text-destructive'"
|
||||
>
|
||||
{{ collection.qdrant?.ok ? $t('memoryProvider.collectionHealthy') : $t('memoryProvider.collectionUnavailable') }}
|
||||
{{ collection.qdrant?.ok ? $t('memory.collectionHealthy') : $t('memory.collectionUnavailable') }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-2xl font-semibold text-foreground">
|
||||
{{ collection.points ?? 0 }}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('memoryProvider.collectionPoints') }}
|
||||
{{ $t('memory.collectionPoints') }}
|
||||
</p>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ collection.exists ? $t('memoryProvider.collectionExists') : $t('memoryProvider.collectionMissing') }}
|
||||
{{ collection.exists ? $t('memory.collectionExists') : $t('memory.collectionMissing') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -351,7 +351,7 @@ async function handleSave() {
|
||||
if (curProvider?.value && data) {
|
||||
Object.assign(curProvider.value, data)
|
||||
}
|
||||
toast.success(t('memoryProvider.saveSuccess'))
|
||||
toast.success(t('memory.saveSuccess'))
|
||||
queryCache.invalidateQueries({ key: ['memory-providers'] })
|
||||
} catch (error) {
|
||||
console.error('Failed to save:', error)
|
||||
@@ -369,11 +369,11 @@ async function handleDelete() {
|
||||
path: { id: curProvider.value.id! },
|
||||
throwOnError: true,
|
||||
})
|
||||
toast.success(t('memoryProvider.deleteSuccess'))
|
||||
toast.success(t('memory.deleteSuccess'))
|
||||
queryCache.invalidateQueries({ key: ['memory-providers'] })
|
||||
} catch (error) {
|
||||
console.error('Failed to delete:', error)
|
||||
toast.error(t('memoryProvider.deleteFailed'))
|
||||
toast.error(t('memory.deleteFailed'))
|
||||
} finally {
|
||||
deleteLoading.value = false
|
||||
}
|
||||
@@ -100,8 +100,8 @@ const openStatus = reactive({ addOpen: false })
|
||||
<FontAwesomeIcon :icon="['fas', 'brain']" />
|
||||
</EmptyMedia>
|
||||
</EmptyHeader>
|
||||
<EmptyTitle>{{ $t('memoryProvider.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('memoryProvider.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyTitle>{{ $t('memory.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('memory.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyContent>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -112,7 +112,7 @@ const openStatus = reactive({ addOpen: false })
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-2"
|
||||
/>
|
||||
{{ $t('memoryProvider.add') }}
|
||||
{{ $t('memory.add') }}
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<FormDialogShell
|
||||
v-model:open="open"
|
||||
:title="$t('ttsProvider.addModel')"
|
||||
:title="$t('speech.addModel')"
|
||||
:cancel-text="$t('common.cancel')"
|
||||
:submit-text="$t('ttsProvider.addModel')"
|
||||
:submit-text="$t('speech.addModel')"
|
||||
:submit-disabled="(form.meta.value.valid === false) || isLoading"
|
||||
:loading="isLoading"
|
||||
@submit="handleCreate"
|
||||
>
|
||||
<template #trigger>
|
||||
<Button variant="default">
|
||||
{{ $t('ttsProvider.addModel') }}
|
||||
{{ $t('speech.addModel') }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #body>
|
||||
@@ -21,13 +21,13 @@
|
||||
>
|
||||
<FormItem>
|
||||
<Label :for="componentField.id || 'tts-model-id'">
|
||||
{{ $t('ttsProvider.modelId') }}
|
||||
{{ $t('speech.modelId') }}
|
||||
</Label>
|
||||
<FormControl>
|
||||
<Input
|
||||
:id="componentField.id || 'tts-model-id'"
|
||||
type="text"
|
||||
:placeholder="$t('ttsProvider.modelIdPlaceholder')"
|
||||
:placeholder="$t('speech.modelIdPlaceholder')"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
+4
-4
@@ -2,9 +2,9 @@
|
||||
<section>
|
||||
<FormDialogShell
|
||||
v-model:open="open"
|
||||
:title="$t('ttsProvider.add')"
|
||||
:title="$t('speech.add')"
|
||||
:cancel-text="$t('common.cancel')"
|
||||
:submit-text="$t('ttsProvider.add')"
|
||||
:submit-text="$t('speech.add')"
|
||||
:submit-disabled="(form.meta.value.valid === false) || isLoading"
|
||||
:loading="isLoading"
|
||||
@submit="handleCreate"
|
||||
@@ -17,7 +17,7 @@
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('ttsProvider.add') }}
|
||||
/> {{ $t('speech.add') }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #body>
|
||||
@@ -46,7 +46,7 @@
|
||||
>
|
||||
<FormItem>
|
||||
<Label :for="componentField.id || 'tts-provider-type'">
|
||||
{{ $t('ttsProvider.providerType') }}
|
||||
{{ $t('speech.providerType') }}
|
||||
</Label>
|
||||
<FormControl>
|
||||
<Select v-bind="componentField">
|
||||
+15
-15
@@ -3,7 +3,7 @@
|
||||
<template v-if="caps">
|
||||
<!-- Language -->
|
||||
<div class="space-y-2">
|
||||
<Label for="tts-lang">{{ $t('ttsProvider.fields.language') }}</Label>
|
||||
<Label for="tts-lang">{{ $t('speech.fields.language') }}</Label>
|
||||
<Select
|
||||
:model-value="configData.voice_lang ?? ''"
|
||||
@update:model-value="onLangChange"
|
||||
@@ -12,7 +12,7 @@
|
||||
id="tts-lang"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectValue :placeholder="$t('ttsProvider.fields.languagePlaceholder')" />
|
||||
<SelectValue :placeholder="$t('speech.fields.languagePlaceholder')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent class="max-h-60">
|
||||
<SelectItem
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<!-- Voice -->
|
||||
<div class="space-y-2">
|
||||
<Label for="tts-voice">{{ $t('ttsProvider.fields.voice') }}</Label>
|
||||
<Label for="tts-voice">{{ $t('speech.fields.voice') }}</Label>
|
||||
<Select
|
||||
:model-value="configData.voice_id ?? ''"
|
||||
@update:model-value="(val) => configData.voice_id = val"
|
||||
@@ -37,7 +37,7 @@
|
||||
id="tts-voice"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectValue :placeholder="$t('ttsProvider.fields.voicePlaceholder')" />
|
||||
<SelectValue :placeholder="$t('speech.fields.voicePlaceholder')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent class="max-h-60">
|
||||
<SelectItem
|
||||
@@ -56,7 +56,7 @@
|
||||
v-if="caps.formats && caps.formats.length > 0"
|
||||
class="space-y-2"
|
||||
>
|
||||
<Label for="tts-format">{{ $t('ttsProvider.fields.format') }}</Label>
|
||||
<Label for="tts-format">{{ $t('speech.fields.format') }}</Label>
|
||||
<Select
|
||||
:model-value="configData.format ?? ''"
|
||||
@update:model-value="(val) => configData.format = val"
|
||||
@@ -65,7 +65,7 @@
|
||||
id="tts-format"
|
||||
class="w-full"
|
||||
>
|
||||
<SelectValue :placeholder="$t('ttsProvider.fields.formatPlaceholder')" />
|
||||
<SelectValue :placeholder="$t('speech.fields.formatPlaceholder')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
@@ -84,9 +84,9 @@
|
||||
v-if="caps.speed"
|
||||
class="space-y-2"
|
||||
>
|
||||
<Label>{{ $t('ttsProvider.fields.speed') }}</Label>
|
||||
<Label>{{ $t('speech.fields.speed') }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('ttsProvider.fields.speedDescription', { default: caps.speed.default ?? 1 }) }}
|
||||
{{ $t('speech.fields.speedDescription', { default: caps.speed.default ?? 1 }) }}
|
||||
</p>
|
||||
<div v-if="caps.speed.options && caps.speed.options.length > 0">
|
||||
<Select
|
||||
@@ -130,9 +130,9 @@
|
||||
v-if="caps.pitch"
|
||||
class="space-y-2"
|
||||
>
|
||||
<Label>{{ $t('ttsProvider.fields.pitch') }}</Label>
|
||||
<Label>{{ $t('speech.fields.pitch') }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ $t('ttsProvider.fields.pitchDescription', { default: caps.pitch.default ?? 0 }) }}
|
||||
{{ $t('speech.fields.pitchDescription', { default: caps.pitch.default ?? 0 }) }}
|
||||
</p>
|
||||
<div
|
||||
v-if="caps.pitch.options && caps.pitch.options.length > 0"
|
||||
@@ -178,7 +178,7 @@
|
||||
v-else
|
||||
class="text-xs text-muted-foreground"
|
||||
>
|
||||
{{ $t('ttsProvider.noCapabilities') }}
|
||||
{{ $t('speech.noCapabilities') }}
|
||||
</div>
|
||||
|
||||
<Separator class="my-3" />
|
||||
@@ -186,12 +186,12 @@
|
||||
<!-- Test Synthesis -->
|
||||
<div class="space-y-3">
|
||||
<h4 class="text-xs font-medium">
|
||||
{{ $t('ttsProvider.test.title') }}
|
||||
{{ $t('speech.test.title') }}
|
||||
</h4>
|
||||
<div class="relative">
|
||||
<Textarea
|
||||
v-model="testText"
|
||||
:placeholder="$t('ttsProvider.test.placeholder')"
|
||||
:placeholder="$t('speech.test.placeholder')"
|
||||
:maxlength="maxTestTextLen"
|
||||
rows="2"
|
||||
class="resize-none"
|
||||
@@ -213,7 +213,7 @@
|
||||
:icon="['fas', 'play']"
|
||||
class="mr-1.5"
|
||||
/>
|
||||
{{ $t('ttsProvider.test.generate') }}
|
||||
{{ $t('speech.test.generate') }}
|
||||
</LoadingButton>
|
||||
<span
|
||||
v-if="testError"
|
||||
@@ -398,7 +398,7 @@ async function handleTest() {
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 50))
|
||||
audioEl.value?.play()
|
||||
} catch (e: unknown) {
|
||||
const msg = e instanceof Error ? e.message : t('ttsProvider.test.failed')
|
||||
const msg = e instanceof Error ? e.message : t('speech.test.failed')
|
||||
testError.value = msg
|
||||
toast.error(msg)
|
||||
} finally {
|
||||
+6
-6
@@ -47,7 +47,7 @@
|
||||
<section>
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xs font-medium">
|
||||
{{ $t('ttsProvider.models') }}
|
||||
{{ $t('speech.models') }}
|
||||
</h3>
|
||||
<div
|
||||
v-if="curProviderId"
|
||||
@@ -61,7 +61,7 @@
|
||||
@click="handleImportModels"
|
||||
>
|
||||
<FontAwesomeIcon :icon="['fas', 'file-import']" />
|
||||
{{ $t('ttsProvider.importModels') }}
|
||||
{{ $t('speech.importModels') }}
|
||||
</LoadingButton>
|
||||
<AddTtsModel
|
||||
:provider-id="curProviderId"
|
||||
@@ -74,7 +74,7 @@
|
||||
v-if="providerModels.length === 0"
|
||||
class="text-xs text-muted-foreground py-4 text-center"
|
||||
>
|
||||
{{ $t('ttsProvider.noModels') }}
|
||||
{{ $t('speech.noModels') }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -119,7 +119,7 @@
|
||||
|
||||
<section class="flex justify-end mt-6 gap-4">
|
||||
<ConfirmPopover
|
||||
:message="$t('ttsProvider.deleteConfirm')"
|
||||
:message="$t('speech.deleteConfirm')"
|
||||
:loading="deleteLoading"
|
||||
@confirm="handleDelete"
|
||||
>
|
||||
@@ -289,11 +289,11 @@ async function handleImportModels() {
|
||||
headers: authHeaders(),
|
||||
})
|
||||
if (!resp.ok) throw new Error('Import failed')
|
||||
toast.success(t('ttsProvider.importSuccess'))
|
||||
toast.success(t('speech.importSuccess'))
|
||||
refreshModels()
|
||||
queryCache.invalidateQueries({ key: ['tts-models'] })
|
||||
} catch (e: unknown) {
|
||||
toast.error(e instanceof Error ? e.message : t('ttsProvider.importFailed'))
|
||||
toast.error(e instanceof Error ? e.message : t('speech.importFailed'))
|
||||
} finally {
|
||||
importLoading.value = false
|
||||
}
|
||||
@@ -103,8 +103,8 @@ const openStatus = reactive({ addOpen: false })
|
||||
<FontAwesomeIcon :icon="['fas', 'volume-high']" />
|
||||
</EmptyMedia>
|
||||
</EmptyHeader>
|
||||
<EmptyTitle>{{ $t('ttsProvider.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('ttsProvider.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyTitle>{{ $t('speech.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('speech.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyContent>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -113,7 +113,7 @@ const openStatus = reactive({ addOpen: false })
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('ttsProvider.add') }}
|
||||
/> {{ $t('speech.add') }}
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
+6
-6
@@ -2,9 +2,9 @@
|
||||
<section>
|
||||
<FormDialogShell
|
||||
v-model:open="open"
|
||||
:title="$t('searchProvider.add')"
|
||||
:title="$t('webSearch.add')"
|
||||
:cancel-text="$t('common.cancel')"
|
||||
:submit-text="$t('searchProvider.add')"
|
||||
:submit-text="$t('webSearch.add')"
|
||||
:submit-disabled="(form.meta.value.valid === false) || isLoading"
|
||||
:loading="isLoading"
|
||||
@submit="handleCreate"
|
||||
@@ -17,7 +17,7 @@
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('searchProvider.add') }}
|
||||
/> {{ $t('webSearch.add') }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #body>
|
||||
@@ -53,14 +53,14 @@
|
||||
class="mb-2"
|
||||
:for="componentField.id || 'search-provider-create-type'"
|
||||
>
|
||||
{{ $t('searchProvider.provider') }}
|
||||
{{ $t('webSearch.provider') }}
|
||||
</Label>
|
||||
<FormControl>
|
||||
<Select v-bind="componentField">
|
||||
<SelectTrigger
|
||||
:id="componentField.id || 'search-provider-create-type'"
|
||||
class="w-full"
|
||||
:aria-label="$t('searchProvider.provider')"
|
||||
:aria-label="$t('webSearch.provider')"
|
||||
>
|
||||
<SelectValue :placeholder="$t('common.typePlaceholder')" />
|
||||
</SelectTrigger>
|
||||
@@ -71,7 +71,7 @@
|
||||
:key="type"
|
||||
:value="type"
|
||||
>
|
||||
{{ $t(`searchProvider.providerNames.${type}`, type) }}
|
||||
{{ $t(`webSearch.providerNames.${type}`, type) }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
+2
-2
@@ -82,13 +82,13 @@
|
||||
v-else-if="form.values.provider"
|
||||
class="text-xs text-muted-foreground"
|
||||
>
|
||||
{{ $t('searchProvider.unsupportedProvider') }}
|
||||
{{ $t('webSearch.unsupportedProvider') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="flex justify-end mt-4 gap-4">
|
||||
<ConfirmPopover
|
||||
:message="$t('searchProvider.deleteConfirm')"
|
||||
:message="$t('webSearch.deleteConfirm')"
|
||||
:loading="deleteLoading"
|
||||
@confirm="deleteProvider"
|
||||
>
|
||||
+3
-3
@@ -114,8 +114,8 @@ const openStatus = reactive({
|
||||
<FontAwesomeIcon :icon="['fas', 'globe']" />
|
||||
</EmptyMedia>
|
||||
</EmptyHeader>
|
||||
<EmptyTitle>{{ $t('searchProvider.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('searchProvider.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyTitle>{{ $t('webSearch.emptyTitle') }}</EmptyTitle>
|
||||
<EmptyDescription>{{ $t('webSearch.emptyDescription') }}</EmptyDescription>
|
||||
<EmptyContent>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -124,7 +124,7 @@ const openStatus = reactive({
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'plus']"
|
||||
class="mr-1"
|
||||
/> {{ $t('searchProvider.add') }}
|
||||
/> {{ $t('webSearch.add') }}
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
+25
-25
@@ -58,51 +58,51 @@ const routes = [
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'models',
|
||||
path: 'models',
|
||||
component: () => import('@/pages/models/index.vue'),
|
||||
name: 'providers',
|
||||
path: 'providers',
|
||||
component: () => import('@/pages/providers/index.vue'),
|
||||
meta: {
|
||||
breadcrumb: i18nRef('sidebar.models'),
|
||||
breadcrumb: i18nRef('sidebar.providers'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'search-providers',
|
||||
path: 'search-providers',
|
||||
component: () => import('@/pages/search-providers/index.vue'),
|
||||
name: 'web-search',
|
||||
path: 'web-search',
|
||||
component: () => import('@/pages/web-search/index.vue'),
|
||||
meta: {
|
||||
breadcrumb: i18nRef('sidebar.searchProvider'),
|
||||
breadcrumb: i18nRef('sidebar.webSearch'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'memory-providers',
|
||||
path: 'memory-providers',
|
||||
component: () => import('@/pages/memory-providers/index.vue'),
|
||||
name: 'memory',
|
||||
path: 'memory',
|
||||
component: () => import('@/pages/memory/index.vue'),
|
||||
meta: {
|
||||
breadcrumb: i18nRef('sidebar.memoryProvider'),
|
||||
breadcrumb: i18nRef('sidebar.memory'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'tts-providers',
|
||||
path: 'tts-providers',
|
||||
component: () => import('@/pages/tts-providers/index.vue'),
|
||||
name: 'speech',
|
||||
path: 'speech',
|
||||
component: () => import('@/pages/speech/index.vue'),
|
||||
meta: {
|
||||
breadcrumb: i18nRef('sidebar.ttsProvider'),
|
||||
breadcrumb: i18nRef('sidebar.speech'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'email-providers',
|
||||
path: 'email-providers',
|
||||
component: () => import('@/pages/email-providers/index.vue'),
|
||||
name: 'email',
|
||||
path: 'email',
|
||||
component: () => import('@/pages/email/index.vue'),
|
||||
meta: {
|
||||
breadcrumb: i18nRef('sidebar.emailProvider'),
|
||||
breadcrumb: i18nRef('sidebar.email'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'browser-contexts',
|
||||
path: 'browser-contexts',
|
||||
component: () => import('@/pages/browser-contexts/index.vue'),
|
||||
name: 'browser',
|
||||
path: 'browser',
|
||||
component: () => import('@/pages/browser/index.vue'),
|
||||
meta: {
|
||||
breadcrumb: i18nRef('sidebar.browserContexts'),
|
||||
breadcrumb: i18nRef('sidebar.browser'),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -114,7 +114,7 @@ const routes = [
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
name: 'profile',
|
||||
path: 'profile',
|
||||
component: () => import('@/pages/settings/index.vue'),
|
||||
meta: {
|
||||
|
||||
Reference in New Issue
Block a user