From be92535c989f3309b128f55c879f671f7d6ca945 Mon Sep 17 00:00:00 2001 From: liucong2013 <10938633+liucong2013@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:48:58 +0000 Subject: [PATCH] fix(openai): polish provider list overlays and i18n --- src/components/common/PageTransition.tsx | 5 +- src/components/common/PageTransitionLayer.ts | 7 +- .../providers/OpenAISection/OpenAISection.tsx | 91 ++++++++++--------- src/i18n/locales/en.json | 3 + src/i18n/locales/zh-CN.json | 3 + src/i18n/locales/zh-TW.json | 8 ++ src/pages/AiProvidersPage.module.scss | 6 +- src/styles/layout.scss | 2 +- 8 files changed, 76 insertions(+), 49 deletions(-) diff --git a/src/components/common/PageTransition.tsx b/src/components/common/PageTransition.tsx index 452d56a..5561275 100644 --- a/src/components/common/PageTransition.tsx +++ b/src/components/common/PageTransition.tsx @@ -391,7 +391,10 @@ export function PageTransition({ } > {render(layer.location)} diff --git a/src/components/common/PageTransitionLayer.ts b/src/components/common/PageTransitionLayer.ts index 98f5898..0ab66c3 100644 --- a/src/components/common/PageTransitionLayer.ts +++ b/src/components/common/PageTransitionLayer.ts @@ -5,15 +5,16 @@ export type LayerStatus = 'current' | 'exiting' | 'stacked'; export type PageTransitionLayerContextValue = { status: LayerStatus; isCurrentLayer: boolean; + isAnimating: boolean; }; export const PageTransitionLayerContext = createContext(null); export const PAGE_TRANSITION_LAYER_CONTEXT_VALUES: Record = { - current: { status: 'current', isCurrentLayer: true }, - stacked: { status: 'stacked', isCurrentLayer: false }, - exiting: { status: 'exiting', isCurrentLayer: false }, + current: { status: 'current', isCurrentLayer: true, isAnimating: false }, + stacked: { status: 'stacked', isCurrentLayer: false, isAnimating: false }, + exiting: { status: 'exiting', isCurrentLayer: false, isAnimating: false }, }; export function usePageTransitionLayer() { diff --git a/src/components/providers/OpenAISection/OpenAISection.tsx b/src/components/providers/OpenAISection/OpenAISection.tsx index 6c51ecc..7b7c766 100644 --- a/src/components/providers/OpenAISection/OpenAISection.tsx +++ b/src/components/providers/OpenAISection/OpenAISection.tsx @@ -16,6 +16,7 @@ import { import { type UsageDetailsByAuthIndex, type UsageDetailsBySource } from '@/utils/usageIndex'; import styles from '@/pages/AiProvidersPage.module.scss'; import { ProviderStatusBar } from '../ProviderStatusBar'; +import { usePageTransitionLayer } from '@/components/common/PageTransitionLayer'; import { collectOpenAIProviderUsageDetails, getOpenAIProviderKey, @@ -68,6 +69,8 @@ export function OpenAISection({ onDelete, }: OpenAISectionProps) { const { t } = useTranslation(); + const pageTransitionLayer = usePageTransitionLayer(); + const isTransitionAnimating = pageTransitionLayer?.isAnimating ?? false; const actionsDisabled = disableControls || loading || isSwitching; const [sortOption, setSortOption] = useState('priority'); const [sortDirection, setSortDirection] = useState('asc'); @@ -85,7 +88,13 @@ export function OpenAISection({ const topDropdownRef = useRef(null); const floatingDropdownRef = useRef(null); + const shouldRenderFloatingToolbar = !isTransitionAnimating && floatingToolbarStyle.visible; + useEffect(() => { + if (isTransitionAnimating) { + return; + } + const updateFloatingToolbar = () => { const section = sectionRef.current; const anchor = topToolbarAnchorRef.current; @@ -131,7 +140,7 @@ export function OpenAISection({ window.removeEventListener('resize', updateFloatingToolbar); window.removeEventListener('scroll', updateFloatingToolbar, true); }; - }, [configs.length, isDropdownOpen, selectedModels, sortDirection, sortOption]); + }, [configs.length, isDropdownOpen, isTransitionAnimating, selectedModels, sortDirection, sortOption]); useEffect(() => { if (!isDropdownOpen) { @@ -239,11 +248,11 @@ export function OpenAISection({ const providerStats = sortOption === 'recent-success' ? new Map( - sorted.map(({ config }) => [ - config, - getOpenAIProviderStats(config, keyStats), - ]) - ) + sorted.map(({ config }) => [ + config, + getOpenAIProviderStats(config, keyStats), + ]) + ) : null; switch (sortOption) { @@ -333,7 +342,7 @@ export function OpenAISection({ ); const renderToolbar = (isFloating = false) => { - const isActiveToolbar = isFloating === floatingToolbarStyle.visible; + const isActiveToolbar = isFloating === shouldRenderFloatingToolbar; const dropdownClassName = dropdownLayout.openAbove ? `${styles.modelDropdownList} ${styles.modelDropdownListAbove}` : styles.modelDropdownList; @@ -536,7 +545,7 @@ export function OpenAISection({ ) : null} {provider.testModel && (
- Test Model: + {t('ai_providers.openai_test_model')}: {provider.testModel}
)} @@ -570,7 +579,7 @@ export function OpenAISection({ extra={
{renderToolbar(false)}
@@ -579,42 +588,42 @@ export function OpenAISection({ {loading && sortedConfigs.length === 0 ? (
{t('common.loading')}
) : configs.length > 0 && sortedConfigs.length === 0 ? ( - - {t('ai_providers.model_search_clear')} - - )} - /> + + {t('ai_providers.model_search_clear')} + + )} + /> ) : sortedConfigs.length === 0 ? ( - - ) : ( -
{sortedConfigs.map(renderProviderCard)}
- )} + + ) : ( +
{sortedConfigs.map(renderProviderCard)}
+ )} - {typeof document !== 'undefined' && floatingToolbarStyle.visible + {typeof document !== 'undefined' && shouldRenderFloatingToolbar ? createPortal( -
-
-
{renderStaticTitle()}
- {renderToolbar(true)} -
-
, - document.body - ) +
+
+
{renderStaticTitle()}
+ {renderToolbar(true)} +
+
, + document.body + ) : null} ); diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index e5c5feb..2d3ddae 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -405,8 +405,11 @@ "openai_filtered_empty_title": "No matching providers", "openai_filtered_empty_desc": "No providers match the current model filter. Clear the filter and try again.", "sort_by_name": "Sort by Name", + "sort_ascending": "Sort ascending", "sort_by_priority": "Sort by Priority", "sort_by_recent_success": "Sort by Recent Success", + "sort_descending": "Sort descending", + "openai_test_model": "Test Model", "openai_add_modal_title": "Add OpenAI Compatible Provider", "openai_add_modal_name_label": "Provider Name:", "openai_add_modal_name_placeholder": "e.g.: openrouter", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 02046f6..756ba9f 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -405,8 +405,11 @@ "openai_filtered_empty_title": "没有匹配的提供商", "openai_filtered_empty_desc": "当前模型筛选下没有匹配的提供商,请清除筛选后重试。", "sort_by_name": "按名称排序", + "sort_ascending": "升序排序", "sort_by_priority": "按优先级排序", "sort_by_recent_success": "按最近成功数排序", + "sort_descending": "降序排序", + "openai_test_model": "测试模型", "openai_add_modal_title": "添加OpenAI兼容提供商", "openai_add_modal_name_label": "提供商名称:", "openai_add_modal_name_placeholder": "例如: openrouter", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 7db6685..fee12b4 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -402,6 +402,14 @@ "openai_add_button": "新增供應商", "openai_empty_title": "暫無 OpenAI 相容供應商", "openai_empty_desc": "點擊上方按鈕新增第一個供應商", + "openai_filtered_empty_title": "沒有匹配的供應商", + "openai_filtered_empty_desc": "目前模型篩選下沒有匹配的供應商,請清除篩選後再試一次。", + "sort_by_name": "依名稱排序", + "sort_ascending": "升冪排序", + "sort_by_priority": "依優先順序排序", + "sort_by_recent_success": "依最近成功排序", + "sort_descending": "降冪排序", + "openai_test_model": "測試模型", "openai_add_modal_title": "新增 OpenAI 相容供應商", "openai_add_modal_name_label": "供應商名稱:", "openai_add_modal_name_placeholder": "例如: openrouter", diff --git a/src/pages/AiProvidersPage.module.scss b/src/pages/AiProvidersPage.module.scss index ec09b26..46ed02a 100644 --- a/src/pages/AiProvidersPage.module.scss +++ b/src/pages/AiProvidersPage.module.scss @@ -255,8 +255,8 @@ height: 18px; padding: 0; border: none; - background: rgba(0, 0, 0, 0.15); - color: #000; + background: var(--bg-secondary); + color: var(--text-primary); font-size: 18px; line-height: 1; cursor: pointer; @@ -265,7 +265,7 @@ flex-shrink: 0; &:hover { - background: rgba(0, 0, 0, 0.3); + background: var(--bg-tertiary); } } diff --git a/src/styles/layout.scss b/src/styles/layout.scss index 7c38ab0..c2f3494 100644 --- a/src/styles/layout.scss +++ b/src/styles/layout.scss @@ -31,7 +31,7 @@ border-bottom: 1px solid var(--border-color); position: sticky; top: 0; - z-index: 10; + z-index: $z-dropdown + 1; width: 100%; @media (max-width: $breakpoint-mobile) {