diff --git a/src/renderer/App.css b/src/renderer/App.css index 8ff5f90..53ee9fd 100644 --- a/src/renderer/App.css +++ b/src/renderer/App.css @@ -116,6 +116,83 @@ } } +/* Provider ZenMux promotion banner */ +.provider-ad-banner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 10px 14px; + border: 1px solid rgba(22, 119, 255, 0.22); + border-radius: 12px; + background: + linear-gradient(135deg, rgba(22, 119, 255, 0.1), rgba(114, 46, 209, 0.08)), + #ffffff; + box-shadow: 0 6px 18px rgba(22, 119, 255, 0.08); +} + +.provider-ad-banner__content { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + flex-wrap: wrap; +} + +.provider-ad-banner__brand { + color: #0958d9; + font-weight: 600; +} + +.provider-ad-banner__text { + color: #5a6072; +} + +.provider-ad-banner__link { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + min-height: 30px; + padding: 3px 12px; + border: 1px solid rgba(22, 119, 255, 0.28); + border-radius: 999px; + color: #0958d9; + font-weight: 500; + line-height: 1.4; + text-decoration: none; + background: rgba(255, 255, 255, 0.72); + box-shadow: none; + transition: + border-color 0.2s ease, + color 0.2s ease, + background 0.2s ease; +} + +.provider-ad-banner__link:hover, +.provider-ad-banner__link:focus { + color: #1677ff; + text-decoration: none; + border-color: rgba(22, 119, 255, 0.45); + background: rgba(22, 119, 255, 0.08); +} + +.provider-ad-banner__link:focus-visible { + outline: 3px solid rgba(22, 119, 255, 0.22); + outline-offset: 2px; +} + +@media (max-width: 720px) { + .provider-ad-banner { + align-items: flex-start; + flex-direction: column; + } + + .provider-ad-banner__link { + width: 100%; + } +} + /* Settings Tabs - tab栏固定,内容区域滚动 */ .settings-tabs .ant-tabs-content-holder { flex: 1 1 auto; diff --git a/src/renderer/components/Provider.tsx b/src/renderer/components/Provider.tsx index eef7482..26bf84a 100644 --- a/src/renderer/components/Provider.tsx +++ b/src/renderer/components/Provider.tsx @@ -490,6 +490,26 @@ const Provider: React.FC = ({ +
+
+ + {t('ad_banner.title')} + + + {t('ad_banner.description')} + +
+ + {t('ad_banner.cta')} + +
+ {t('model_config.title')} diff --git a/src/renderer/components/__tests__/Provider.test.tsx b/src/renderer/components/__tests__/Provider.test.tsx index c1d5750..40b9f6b 100644 --- a/src/renderer/components/__tests__/Provider.test.tsx +++ b/src/renderer/components/__tests__/Provider.test.tsx @@ -21,6 +21,10 @@ vi.mock('react-i18next', () => ({ 'model_config.id_placeholder': 'Model ID', 'model_config.add_button': 'Add Model', 'model_config.warning': 'Warning: Make sure the model ID matches the provider API', + 'ad_banner.title': 'ZenMux: reliable enterprise-grade LLM API service', + 'ad_banner.description': 'Get an extra 5–8% bonus on your first top-up', + 'ad_banner.cta': 'Get it now', + 'ad_banner.cta_aria': 'Get ZenMux enterprise-grade LLM API service', 'actions.delete_provider': 'Delete Provider', 'messages.update_success': 'Updated successfully', 'messages.update_failed': 'Update failed', @@ -160,6 +164,26 @@ describe('Provider', () => { expect(screen.getByText(/Protocol:.*openai/)).toBeInTheDocument() }) }) + + it('should display ZenMux ad banner with external link', async () => { + render( + + + + ) + + await waitFor(() => { + expect(screen.getByText('ZenMux: reliable enterprise-grade LLM API service')).toBeInTheDocument() + }) + + expect(screen.getByText('Get an extra 5–8% bonus on your first top-up')).toBeInTheDocument() + const adLink = screen.getByRole('link', { + name: 'Get ZenMux enterprise-grade LLM API service' + }) + expect(adLink).toHaveAttribute('href', 'https://zenmux.ai/invite/9H70CU') + expect(adLink).toHaveAttribute('target', '_blank') + expect(adLink).toHaveAttribute('rel', 'noreferrer') + }) }) describe('API Key Input', () => { diff --git a/src/renderer/locales/ar-SA/provider.json b/src/renderer/locales/ar-SA/provider.json index e3ca786..a8521f4 100644 --- a/src/renderer/locales/ar-SA/provider.json +++ b/src/renderer/locales/ar-SA/provider.json @@ -16,6 +16,12 @@ "add_button": "إضافة نموذج", "warning": "ملاحظة: يرجى إضافة نماذج تدعم التعرف على الرؤية والتأكد من صحة معرف النموذج، وإلا لن يعمل النموذج بشكل صحيح!" }, + "ad_banner": { + "title": "ZenMux: خدمة API موثوقة لنماذج اللغة الكبيرة على مستوى المؤسسات", + "description": "احصل على هدية إضافية بنسبة 5–8% عند أول شحن", + "cta": "احصل عليها", + "cta_aria": "احصل على خدمة API المؤسسية لنماذج اللغة الكبيرة ZenMux" + }, "actions": { "delete_provider": "حذف المزود", "delete_model_title": "حذف", diff --git a/src/renderer/locales/en-US/provider.json b/src/renderer/locales/en-US/provider.json index 9ec02ed..b341311 100644 --- a/src/renderer/locales/en-US/provider.json +++ b/src/renderer/locales/en-US/provider.json @@ -16,6 +16,12 @@ "add_button": "Add Model", "warning": "Note: Please add models that support vision recognition and ensure the Model ID is correct, otherwise the model will not work properly!" }, + "ad_banner": { + "title": "ZenMux: reliable enterprise-grade LLM API service", + "description": "Get an extra 5–8% bonus on your first top-up", + "cta": "Get it now", + "cta_aria": "Get ZenMux enterprise-grade LLM API service" + }, "actions": { "delete_provider": "Delete Provider", "delete_model_title": "Delete", diff --git a/src/renderer/locales/fa-IR/provider.json b/src/renderer/locales/fa-IR/provider.json index 9abdcf3..147b93b 100644 --- a/src/renderer/locales/fa-IR/provider.json +++ b/src/renderer/locales/fa-IR/provider.json @@ -16,6 +16,12 @@ "add_button": "افزودن مدل", "warning": "توجه: لطفاً مدل‌هایی که از تشخیص تصویر پشتیبانی می‌کنند اضافه کنید و مطمئن شوید که شناسه مدل صحیح است. در غیر این صورت مدل به درستی کار نخواهد کرد!" }, + "ad_banner": { + "title": "ZenMux: سرویس API قابل اعتماد مدل‌های زبانی بزرگ در سطح سازمانی", + "description": "برای اولین شارژ 5 تا 8٪ هدیه اضافه دریافت کنید", + "cta": "دریافت کنید", + "cta_aria": "دریافت سرویس API سازمانی مدل‌های زبانی بزرگ ZenMux" + }, "actions": { "delete_provider": "حذف ارائه‌دهنده", "delete_model_title": "حذف", diff --git a/src/renderer/locales/ja-JP/provider.json b/src/renderer/locales/ja-JP/provider.json index 245cd0c..e147feb 100644 --- a/src/renderer/locales/ja-JP/provider.json +++ b/src/renderer/locales/ja-JP/provider.json @@ -16,6 +16,12 @@ "add_button": "モデルを追加", "warning": "注意: 視覚認識に対応しているモデルを追加し、モデルIDが正確であることを確認してください。さもなければモデルは正常に動作しません!" }, + "ad_banner": { + "title": "ZenMux:信頼できるエンタープライズ向け大規模モデルAPIサービス", + "description": "初回チャージで5~8%の追加特典をプレゼント", + "cta": "今すぐ入手", + "cta_aria": "ZenMux のエンタープライズ向け大規模モデルAPIサービスを入手" + }, "actions": { "delete_provider": "プロバイダーを削除", "delete_model_title": "削除", diff --git a/src/renderer/locales/ru-RU/provider.json b/src/renderer/locales/ru-RU/provider.json index 2490eb5..081ad58 100644 --- a/src/renderer/locales/ru-RU/provider.json +++ b/src/renderer/locales/ru-RU/provider.json @@ -16,6 +16,12 @@ "add_button": "Добавить модель", "warning": "Примечание: Пожалуйста, добавьте модели, поддерживающие визуальное распознавание, и убедитесь, что ID модели указан правильно. В противном случае модель не будет работать должным образом!" }, + "ad_banner": { + "title": "ZenMux: надежный корпоративный API-сервис больших моделей", + "description": "Получите дополнительный бонус 5–8% при первом пополнении", + "cta": "Получить", + "cta_aria": "Получить корпоративный API-сервис больших моделей ZenMux" + }, "actions": { "delete_provider": "Удалить провайдера", "delete_model_title": "Удалить", diff --git a/src/renderer/locales/zh-CN/provider.json b/src/renderer/locales/zh-CN/provider.json index 1f44328..1df1b96 100644 --- a/src/renderer/locales/zh-CN/provider.json +++ b/src/renderer/locales/zh-CN/provider.json @@ -16,6 +16,12 @@ "add_button": "添加模型", "warning": "注意:请添加支持视觉识别的模型,并确保模型ID正确,否则模型将无法正常使用!" }, + "ad_banner": { + "title": "ZenMux:可靠的企业级大模型API服务", + "description": "首充可获5~8%额外赠送", + "cta": "点击获取", + "cta_aria": "点击获取 ZenMux 企业级大模型API服务" + }, "actions": { "delete_provider": "删除服务商", "delete_model_title": "删除",