From a912dd473a6282e54d67d3334e3df3fa6ce9cec2 Mon Sep 17 00:00:00 2001 From: lzt404 <2596933790@qq.com> Date: Tue, 28 Jul 2026 14:39:45 +0800 Subject: [PATCH] feat: update API access error messages and remarks handling for improved clarity --- src/i18n/ja.ts | 9 ++++----- src/i18n/resources.ts | 18 ++++++++---------- src/pages/ApiAccessPage.tsx | 32 ++++++++++++++++++++------------ tests/providerConfig.test.ts | 13 +++++++++++++ 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/src/i18n/ja.ts b/src/i18n/ja.ts index f528ce5..9aefe1e 100644 --- a/src/i18n/ja.ts +++ b/src/i18n/ja.ts @@ -522,7 +522,7 @@ export const jaOverrides = { 'apiAccess.error.headerMissingColon': 'カスタムヘッダーの {number} 行目にコロンがありません', 'apiAccess.error.headerInvalid': 'カスタムヘッダーの {number} 行目の形式が無効です', 'apiAccess.error.partialLoad': '一部の接続を読み込めませんでした:{errors}', - 'apiAccess.error.requiredAll': '名前、Base URL、キーを入力してください', + 'apiAccess.error.requiredAll': 'メモ、Base URL、キーを入力してください', 'apiAccess.error.requiredBaseKey': 'Base URL と API キーを入力してください', 'apiAccess.error.requiredKey': 'API キーを入力してください', 'apiAccess.error.baseRequired': '{provider} 接続には Base URL が必要です', @@ -532,7 +532,7 @@ export const jaOverrides = { 'apiAccess.error.remarkInvalid': 'メモは 80 文字以内で、改行や制御文字を含めないでください', 'apiAccess.notice.updated': '接続を更新しました', 'apiAccess.notice.added': '接続を追加しました', - 'apiAccess.deleteConfirm': '「{name}」を削除しますか?', + 'apiAccess.deleteConfirm': '「{remark}」を削除しますか?', 'apiAccess.notice.deleted': '接続を削除しました', 'apiAccess.notice.enabled': '接続を有効にしました', 'apiAccess.notice.disabled': '接続を無効にしました', @@ -540,7 +540,7 @@ export const jaOverrides = { 'apiAccess.count': '接続 {count} 件', 'apiAccess.add': '追加', 'apiAccess.matches': '一致する接続 {count} 件', - 'apiAccess.search': '名前、キー、アドレスを検索', + 'apiAccess.search': 'メモ、キー、アドレスを検索', 'apiAccess.loading': '設定を読み込んでいます', 'apiAccess.empty.filtered': '一致する接続がありません', 'apiAccess.empty.none': '接続設定がありません', @@ -557,7 +557,7 @@ export const jaOverrides = { 'apiAccess.disable': '接続を無効化', 'apiAccess.status.disabled': '無効', 'apiAccess.status.enabled': '有効', - 'apiAccess.toggleAria': '{name} 接続の{action}スイッチ', + 'apiAccess.toggleAria': '{remark} 接続の{action}スイッチ', 'common.edit': '編集', 'apiAccess.error.enterBaseUrl': '先に Base URL を入力してください', 'apiAccess.error.noAvailableModels': '利用可能なモデルが見つかりません', @@ -571,7 +571,6 @@ export const jaOverrides = { 'apiAccess.models.allHint': '現在、上流のすべてのモデルを公開しています', 'apiAccess.dialog.edit': 'API 接続を編集', 'apiAccess.dialog.add': 'API 接続を追加', - 'apiAccess.field.name': '名前', 'apiAccess.field.keysMany': 'API キー(1 行に 1 件)', 'apiAccess.field.key': 'API キー', 'apiAccess.field.remark': 'メモ', diff --git a/src/i18n/resources.ts b/src/i18n/resources.ts index e510e89..a67ce26 100644 --- a/src/i18n/resources.ts +++ b/src/i18n/resources.ts @@ -525,7 +525,7 @@ export const zhCN = { 'apiAccess.error.headerMissingColon': '自定义请求头第 {number} 行缺少冒号', 'apiAccess.error.headerInvalid': '自定义请求头第 {number} 行格式无效', 'apiAccess.error.partialLoad': '部分接入读取失败:{errors}', - 'apiAccess.error.requiredAll': '名称、Base URL 和密钥不能为空', + 'apiAccess.error.requiredAll': '备注、Base URL 和密钥不能为空', 'apiAccess.error.requiredBaseKey': 'Base URL 和 API 密钥不能为空', 'apiAccess.error.requiredKey': 'API 密钥不能为空', 'apiAccess.error.baseRequired': '{provider} 接入必须填写 Base URL', @@ -535,7 +535,7 @@ export const zhCN = { 'apiAccess.error.remarkInvalid': '备注不能超过 80 个字符,且不能包含换行或控制字符', 'apiAccess.notice.updated': '接入已更新', 'apiAccess.notice.added': '接入已新增', - 'apiAccess.deleteConfirm': '确定删除“{name}”吗?', + 'apiAccess.deleteConfirm': '确定删除“{remark}”吗?', 'apiAccess.notice.deleted': '接入已删除', 'apiAccess.notice.enabled': '接入已启用', 'apiAccess.notice.disabled': '接入已停用', @@ -543,7 +543,7 @@ export const zhCN = { 'apiAccess.count': '{count} 个接入', 'apiAccess.add': '新增', 'apiAccess.matches': '{count} 个匹配接入', - 'apiAccess.search': '搜索名称、密钥或地址', + 'apiAccess.search': '搜索备注、密钥或地址', 'apiAccess.loading': '读取配置中', 'apiAccess.empty.filtered': '没有匹配的接入', 'apiAccess.empty.none': '暂无接入配置', @@ -560,7 +560,7 @@ export const zhCN = { 'apiAccess.disable': '停用接入', 'apiAccess.status.disabled': '已停用', 'apiAccess.status.enabled': '已启用', - 'apiAccess.toggleAria': '{name} 接入{action}开关', + 'apiAccess.toggleAria': '{remark} 接入{action}开关', 'common.edit': '编辑', 'apiAccess.error.enterBaseUrl': '请先填写 Base URL', 'apiAccess.error.noAvailableModels': '未发现可用模型', @@ -574,7 +574,6 @@ export const zhCN = { 'apiAccess.models.allHint': '当前开放上游全部模型', 'apiAccess.dialog.edit': '编辑 API 接入', 'apiAccess.dialog.add': '新增 API 接入', - 'apiAccess.field.name': '名称', 'apiAccess.field.keysMany': 'API 密钥(每行一个)', 'apiAccess.field.key': 'API 密钥', 'apiAccess.field.remark': '备注', @@ -1341,7 +1340,7 @@ export const en: Record = { 'apiAccess.error.headerMissingColon': 'Custom header line {number} is missing a colon', 'apiAccess.error.headerInvalid': 'Custom header line {number} is invalid', 'apiAccess.error.partialLoad': 'Some connections could not be loaded: {errors}', - 'apiAccess.error.requiredAll': 'Name, Base URL, and key are required', + 'apiAccess.error.requiredAll': 'Remark, Base URL, and key are required', 'apiAccess.error.requiredBaseKey': 'Base URL and API key are required', 'apiAccess.error.requiredKey': 'API key is required', 'apiAccess.error.baseRequired': 'A Base URL is required for {provider}', @@ -1351,7 +1350,7 @@ export const en: Record = { 'apiAccess.error.remarkInvalid': 'The remark cannot exceed 80 characters or contain line breaks or control characters', 'apiAccess.notice.updated': 'Connection updated', 'apiAccess.notice.added': 'Connection added', - 'apiAccess.deleteConfirm': 'Delete “{name}”?', + 'apiAccess.deleteConfirm': 'Delete “{remark}”?', 'apiAccess.notice.deleted': 'Connection deleted', 'apiAccess.notice.enabled': 'Connection enabled', 'apiAccess.notice.disabled': 'Connection disabled', @@ -1359,7 +1358,7 @@ export const en: Record = { 'apiAccess.count': '{count} connections', 'apiAccess.add': 'Add', 'apiAccess.matches': '{count} matching connections', - 'apiAccess.search': 'Search names, keys, or addresses', + 'apiAccess.search': 'Search remarks, keys, or addresses', 'apiAccess.loading': 'Loading configuration', 'apiAccess.empty.filtered': 'No matching connections', 'apiAccess.empty.none': 'No API connections', @@ -1376,7 +1375,7 @@ export const en: Record = { 'apiAccess.disable': 'Disable connection', 'apiAccess.status.disabled': 'Disabled', 'apiAccess.status.enabled': 'Enabled', - 'apiAccess.toggleAria': '{name} connection {action} switch', + 'apiAccess.toggleAria': '{remark} connection {action} switch', 'common.edit': 'Edit', 'apiAccess.error.enterBaseUrl': 'Enter a Base URL first', 'apiAccess.error.noAvailableModels': 'No available models found', @@ -1390,7 +1389,6 @@ export const en: Record = { 'apiAccess.models.allHint': 'All upstream models are currently open', 'apiAccess.dialog.edit': 'Edit API Connection', 'apiAccess.dialog.add': 'Add API Connection', - 'apiAccess.field.name': 'Name', 'apiAccess.field.keysMany': 'API Keys (one per line)', 'apiAccess.field.key': 'API Key', 'apiAccess.field.remark': 'Remark', diff --git a/src/pages/ApiAccessPage.tsx b/src/pages/ApiAccessPage.tsx index f0b532c..835d154 100644 --- a/src/pages/ApiAccessPage.tsx +++ b/src/pages/ApiAccessPage.tsx @@ -323,7 +323,7 @@ const thinkingLevelsFromModels = (models: ModelOption[]): string[] => { const draftFromRow = (row: ProviderRow): ProviderDraft => ({ name: row.name, apiKey: definitionFor(row.section).openAi ? row.apiKeys.join('\n') : row.apiKey, - remark: row.remark, + remark: row.remark || (definitionFor(row.section).openAi ? row.name : ''), baseUrl: row.baseUrl, priority: row.priority === null ? '' : String(row.priority), models: row.models, @@ -383,11 +383,19 @@ export const createProviderDraft = (category: ProviderCategory): ProviderDraft = return { ...draft, name: 'DeepSeek', + remark: 'DeepSeek', baseUrl: DEEPSEEK_BASE_URL, thinkingLevels: [...DEEPSEEK_THINKING_LEVELS], }; }; +export const applyProviderRemarkIdentity = ( + category: ProviderCategory, + draft: ProviderDraft, +): ProviderDraft => definitionFor(category).openAi + ? { ...draft, name: draft.remark.trim() } + : draft; + export const applyProviderPreset = ( category: ProviderCategory, draft: ProviderDraft, @@ -696,7 +704,7 @@ export function ApiAccessPage() { .filter((row) => { const query = filter.trim().toLowerCase(); if (!query) return true; - return [row.name, row.remark, row.apiKey, row.baseUrl, row.models.map((model) => model.name).join(' ')] + return [row.remark || row.name, row.apiKey, row.baseUrl, row.models.map((model) => model.name).join(' ')] .join(' ') .toLowerCase() .includes(query); @@ -721,7 +729,10 @@ export function ApiAccessPage() { const saveProvider = async (nextDraft: ProviderDraft): Promise => { const definition = activeDefinition; - const preparedDraft = applyProviderPreset(activeCategory, nextDraft); + const preparedDraft = applyProviderRemarkIdentity( + activeCategory, + applyProviderPreset(activeCategory, nextDraft), + ); const baseUrlRequired = definition.openAi || definition.section === 'codex-api-key'; const parsedApiKeys = preparedDraft.apiKey .split(/\r?\n/) @@ -729,7 +740,7 @@ export function ApiAccessPage() { .filter(Boolean); if ( parsedApiKeys.length === 0 - || (definition.openAi && !preparedDraft.name.trim()) + || (definition.openAi && !preparedDraft.remark.trim()) || (baseUrlRequired && !preparedDraft.baseUrl.trim()) ) { setError( @@ -827,7 +838,7 @@ export function ApiAccessPage() { }; const deleteRow = async (row: ProviderRow) => { - if (!window.confirm(t('apiAccess.deleteConfirm', { name: row.name }))) return; + if (!window.confirm(t('apiAccess.deleteConfirm', { remark: row.remark || row.name }))) return; setBusy(true); setError(''); try { @@ -992,7 +1003,7 @@ export function ApiAccessPage() { checked={!row.disabled} onChange={() => void toggleProvider(row)} disabled={busy} - aria-label={t('apiAccess.toggleAria', { name: row.name, action: row.disabled ? t('common.enable') : t('common.disable') })} + aria-label={t('apiAccess.toggleAria', { remark: row.remark || row.name, action: row.disabled ? t('common.enable') : t('common.disable') })} /> @@ -1077,7 +1088,7 @@ function ApiProviderDialog({ && visibleModelOptions.every((model) => selectedModelNames.has(model.name.toLowerCase())); const updateTextField = ( - field: 'name' | 'apiKey' | 'remark' | 'baseUrl' | 'priority' | 'prefix' | 'headersText' | 'excludedModelsText' | 'testModel' | 'cloakMode' | 'cloakSensitiveWordsText', + field: 'apiKey' | 'remark' | 'baseUrl' | 'priority' | 'prefix' | 'headersText' | 'excludedModelsText' | 'testModel' | 'cloakMode' | 'cloakSensitiveWordsText', value: string, ) => { setDraft((current) => ({ ...current, [field]: value })); @@ -1239,18 +1250,15 @@ function ApiProviderDialog({ - {definition.openAi ? ( - - ) : null} +