diff --git a/src/components/AuthFileSettingsDialog.css b/src/components/AuthFileSettingsDialog.css index 9ad2d50..9fb3965 100644 --- a/src/components/AuthFileSettingsDialog.css +++ b/src/components/AuthFileSettingsDialog.css @@ -58,7 +58,8 @@ .credential-alias-list, .credential-header-list { display: grid; gap: 12px; } .credential-alias-card { display: grid; gap: 8px; } .credential-alias-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; gap: 8px; align-items: center; } -.credential-alias-row input { min-width: 0; } +.credential-alias-row input, +.credential-alias-row .agent-model-picker { min-width: 0; } .credential-alias-row-actions { display: flex; align-items: center; gap: 4px; } .credential-alias-row-actions .is-expanded { transform: rotate(180deg); } .credential-alias-details { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--border-color); border-radius: 10px; background: var(--bg-secondary); } @@ -73,6 +74,6 @@ @media (max-width: 560px) { .credential-alias-row { grid-template-columns: minmax(0, 1fr) auto; } - .credential-alias-row input:first-child { grid-column: 1 / -1; } + .credential-alias-row > :first-child { grid-column: 1 / -1; } } diff --git a/src/components/AuthFileSettingsDialog.tsx b/src/components/AuthFileSettingsDialog.tsx index 77c1461..05e1ac1 100644 --- a/src/components/AuthFileSettingsDialog.tsx +++ b/src/components/AuthFileSettingsDialog.tsx @@ -7,7 +7,7 @@ import { loadAuthFileSettings, saveAuthFileSettings, type AuthFileSettingsDraft, import { modelMatchesRule, normalizeOAuthExcludedRules, oauthModelCandidates, oauthModelsFromPayload, setOAuthModelsExcluded, type OAuthModelDefinition } from '../services/oauthModels'; import './AuthFileSettingsDialog.css'; import { CredentialAdvancedFields, CredentialHeadersEditor, credentialProviderKey } from './CredentialAdvancedFields'; -import { modelSearchText } from '../services/modelService'; +import { modelSearchText, type ModelOption } from '../services/modelService'; export function AuthFileSettingsDialog({ name, provider = '', onClose, onSaved }: { name: string; @@ -22,6 +22,7 @@ export function AuthFileSettingsDialog({ name, provider = '', onClose, onSaved } const [original, setOriginal] = useState(null); const [draft, setDraft] = useState(null); const [models, setModels] = useState([]); + const [pickerModels, setPickerModels] = useState([]); const [catalogError, setCatalogError] = useState(''); const [catalogLoading, setCatalogLoading] = useState(true); const [search, setSearch] = useState(''); @@ -59,6 +60,7 @@ export function AuthFileSettingsDialog({ name, provider = '', onClose, onSaved } const catalog = oauthModelsFromPayload(payload); if (!active) return; setModels(catalog); + setPickerModels(catalog.map((model) => ({ name: model.id, alias: model.displayName }))); }).catch((reason: unknown) => { if (active) setCatalogError(reason instanceof Error ? reason.message : String(reason)); }).finally(() => { if (active) setCatalogLoading(false); }); @@ -169,7 +171,7 @@ export function AuthFileSettingsDialog({ name, provider = '', onClose, onSaved } - update('advanced', advanced)} /> + update('advanced', advanced)} />

{t('authFiles.settings.additional')}

update('headers', headers)} /> diff --git a/src/components/CredentialAdvancedFields.tsx b/src/components/CredentialAdvancedFields.tsx index 8309e6f..9a5f177 100644 --- a/src/components/CredentialAdvancedFields.tsx +++ b/src/components/CredentialAdvancedFields.tsx @@ -1,5 +1,7 @@ import { useState } from 'react'; import { ChevronDown, Plus, Undo2, X } from 'lucide-react'; +import { AgentModelPicker } from './AgentModelPicker'; +import type { ModelOption } from '../services/modelService'; import { useI18n } from '../i18n'; import type { MessageKey } from '../i18n/resources'; import { isRecord } from '../services/managementApi'; @@ -42,11 +44,14 @@ function aliasRows(value: unknown): AliasRow[] { } export function CredentialAdvancedFields({ - name, provider = '', advanced, onChange, disabled = false, + name, provider = '', advanced, models = [], modelsLoading = false, modelsError = '', onChange, disabled = false, }: { name: string; provider?: string; advanced: Advanced; + models?: ModelOption[]; + modelsLoading?: boolean; + modelsError?: string; onChange: (advanced: Advanced) => void; disabled?: boolean; }) { @@ -101,8 +106,9 @@ export function CredentialAdvancedFields({ const expanded = expandedAlias === index; return
- updateAlias(index, { name: event.currentTarget.value })} /> + updateAlias(index, { name: value })} /> updateAlias(index, { alias: event.currentTarget.value })} />
diff --git a/src/i18n/ja.ts b/src/i18n/ja.ts index 27413e8..4482133 100644 --- a/src/i18n/ja.ts +++ b/src/i18n/ja.ts @@ -88,6 +88,7 @@ export const jaOverrides = { 'authFiles.settings.aliasRemove': "別名 {index} を削除", 'authFiles.settings.aliasUpstream': "モデル名", 'authFiles.settings.aliasClient': "モデル別名", + 'authFiles.settings.aliasModelPlaceholder': "モデルを検索または入力", 'authFiles.settings.aliasDisplay': "表示名", 'authFiles.settings.aliasFork': "モデル一覧に元のモデルを残す", 'authFiles.settings.aliasForce': "応答のモデル名を別名に書き換える", diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 4e1f1fd..ca1d0b2 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -88,6 +88,7 @@ export const en: Record = { 'authFiles.settings.aliasRemove': "Remove alias {index}", 'authFiles.settings.aliasUpstream': "Model name", 'authFiles.settings.aliasClient': "Model alias", + 'authFiles.settings.aliasModelPlaceholder': "Search or enter a model", 'authFiles.settings.aliasDisplay': "Display name", 'authFiles.settings.aliasFork': "Keep the original model in the model list", 'authFiles.settings.aliasForce': "Rewrite response model names to the alias", diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index 2a95248..c8307ef 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -87,6 +87,7 @@ export const zhCN = { 'authFiles.settings.aliasRemove': "删除别名 {index}", 'authFiles.settings.aliasUpstream': "模型名称", 'authFiles.settings.aliasClient': "模型别名", + 'authFiles.settings.aliasModelPlaceholder': "搜索或输入模型", 'authFiles.settings.aliasDisplay': "显示名称", 'authFiles.settings.aliasFork': "在模型列表中保留原始模型", 'authFiles.settings.aliasForce': "将响应模型名称改写为别名", diff --git a/tests/credentialAliasPicker.test.tsx b/tests/credentialAliasPicker.test.tsx index 38c7513..6e09354 100644 --- a/tests/credentialAliasPicker.test.tsx +++ b/tests/credentialAliasPicker.test.tsx @@ -5,19 +5,18 @@ import { I18nProvider } from '../src/i18n'; const html = renderToStaticMarkup( - {}} /> + {}} /> , ); -describe('credential alias editor', () => { - it('uses plain inputs for the upstream model and client alias', () => { - expect(html).not.toContain('role="combobox"'); +describe('credential alias model picker', () => { + it('uses the credential model list for the upstream model and a plain input for the alias', () => { + expect(html.match(/role="combobox"/g)).toHaveLength(1); expect(html).toContain('aria-label="Model name"'); expect(html).toContain('aria-label="Model alias"'); expect(html).toContain('value="gpt-5"'); expect(html).toContain('value="public-gpt"'); - expect(html).toContain('aria-label="Expand"'); - expect(html).not.toContain('Display name'); - expect(html).not.toContain('Search or enter a model'); + expect(html).toContain('Search or enter a model'); }); });