diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index c731dbea58..df71af1928 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -383,6 +383,7 @@ "confirm": "Confirm", "copied": "Copied", "copy": "Copy", + "copy_failed": "Copy failed", "cut": "Cut", "default": "Default", "delete": "Delete", @@ -2072,14 +2073,14 @@ "auth_failed": "Github Copilot authentication failed.", "auth_success": "GitHub Copilot authentication successful.", "auth_success_title": "Certification successful.", + "code_copied": "Authorization code automatically copied to clipboard", "code_failed": "Failed to obtain Device Code, please try again.", "code_generated_desc": "Please copy the device code into the browser link below.", "code_generated_title": "Obtain Device Code", - "confirm_login": "Excessive use may lead to your Github account being banned, please use it cautiously!!!!", - "confirm_title": "Risk Warning", "connect": "Connect to Github", "custom_headers": "Custom request header", "description": "Your GitHub account needs to subscribe to Copilot.", + "description_detail": "GitHub Copilot is an AI-powered code assistant that requires a valid GitHub Copilot subscription to use", "expand": "Expand", "headers_description": "Custom request headers (JSON format)", "invalid_json": "JSON format error", @@ -2089,8 +2090,20 @@ "logout_success": "Successfully logged out.", "model_setting": "Model settings", "open_verification_first": "Please click the link above to access the verification page.", + "open_verification_page": "Open Authorization Page", "rate_limit": "Rate limiting", - "tooltip": "You need to log in to Github before using Github Copilot" + "start_auth": "Start Authorization", + "step_authorize": "Open Authorization Page", + "step_authorize_desc": "Complete authorization on GitHub", + "step_authorize_detail": "Click the button below to open GitHub authorization page, then enter the copied authorization code", + "step_connect": "Complete Connection", + "step_connect_desc": "Confirm connection to GitHub", + "step_connect_detail": "After completing authorization on GitHub page, click this button to complete the connection", + "step_copy_code": "Copy Authorization Code", + "step_copy_code_desc": "Copy device authorization code", + "step_copy_code_detail": "Authorization code has been automatically copied, you can also copy it manually", + "step_get_code": "Get Authorization Code", + "step_get_code_desc": "Generate device authorization code" }, "delete.content": "Are you sure you want to delete this provider?", "delete.title": "Delete Provider", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 4ccb0b6962..ea6c9b0658 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -383,6 +383,7 @@ "confirm": "確認", "copied": "コピーされました", "copy": "コピー", + "copy_failed": "コピーに失敗しました", "cut": "切り取り", "default": "デフォルト", "delete": "削除", @@ -2072,14 +2073,14 @@ "auth_failed": "Github Copilotの認証に失敗しました。", "auth_success": "Github Copilotの認証が成功しました", "auth_success_title": "認証成功", + "code_copied": "認証コードがクリップボードに自動コピーされました", "code_failed": "デバイスコードの取得に失敗しました。再試行してください。", "code_generated_desc": "デバイスコードを下記のブラウザリンクにコピーしてください。", "code_generated_title": "デバイスコードを取得する", - "confirm_login": "過度使用すると、あなたのGithubアカウントが停止される可能性があるため、慎重に使用してください!!!!", - "confirm_title": "リスク警告", "connect": "GitHubに接続する", "custom_headers": "カスタムリクエストヘッダー", "description": "あなたのGithubアカウントはCopilotを購読する必要があります。", + "description_detail": "GitHub Copilot は AI ベースのコード補助ツールで、有効な GitHub Copilot サブスクリプションが必要です", "expand": "展開", "headers_description": "カスタムリクエストヘッダー(JSONフォーマット)", "invalid_json": "JSONフォーマットエラー", @@ -2089,8 +2090,20 @@ "logout_success": "正常にログアウトしました。", "model_setting": "モデル設定", "open_verification_first": "上のリンクをクリックして、確認ページにアクセスしてください。", + "open_verification_page": "認証ページを開く", "rate_limit": "レート制限", - "tooltip": "Github Copilot を使用するには、まず Github にログインする必要があります。" + "start_auth": "認証を開始", + "step_authorize": "認証ページを開く", + "step_authorize_desc": "GitHub で認証を完了する", + "step_authorize_detail": "下のボタンをクリックして GitHub 認証ページを開き、コピーした認証コードを入力してください", + "step_connect": "接続を完了", + "step_connect_desc": "GitHub への接続を確認", + "step_connect_detail": "GitHub ページで認証が完了したら、このボタンをクリックして接続を完了してください", + "step_copy_code": "認証コードをコピー", + "step_copy_code_desc": "デバイス認証コードをコピー", + "step_copy_code_detail": "認証コードは自動的にコピーされましたが、手動でもコピーできます", + "step_get_code": "認証コードを取得", + "step_get_code_desc": "デバイス認証コードを生成" }, "delete.content": "このプロバイダーを削除してもよろしいですか?", "delete.title": "プロバイダーを削除", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 7e13a78f5a..07379d27c9 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -383,6 +383,7 @@ "confirm": "Подтверждение", "copied": "Скопировано", "copy": "Копировать", + "copy_failed": "Не удалось скопировать", "cut": "Вырезать", "default": "По умолчанию", "delete": "Удалить", @@ -2072,14 +2073,14 @@ "auth_failed": "Github Copilot认证失败", "auth_success": "Github Copilot认证成功", "auth_success_title": "Аутентификация успешна", + "code_copied": "Код авторизации автоматически скопирован в буфер обмена", "code_failed": "Получение кода устройства не удалось, пожалуйста, попробуйте еще раз.", "code_generated_desc": "Пожалуйста, скопируйте код устройства в приведенную ниже ссылку браузера.", "code_generated_title": "Получить код устройства", - "confirm_login": "Чрезмерное использование может привести к блокировке вашего Github, будьте осторожны!!!!", - "confirm_title": "Предупреждение о рисках", "connect": "Подключить Github", "custom_headers": "Пользовательские заголовки запроса", "description": "Ваша учетная запись Github должна подписаться на Copilot.", + "description_detail": "GitHub Copilot — это помощник по коду на базе ИИ, для использования которого требуется действующая подписка GitHub Copilot", "expand": "развернуть", "headers_description": "Пользовательские заголовки запроса (формат json)", "invalid_json": "Ошибка формата JSON", @@ -2089,8 +2090,20 @@ "logout_success": "Успешно вышел", "model_setting": "Настройки модели", "open_verification_first": "Пожалуйста, сначала щелкните по ссылке выше, чтобы перейти на страницу проверки.", + "open_verification_page": "Открыть страницу авторизации", "rate_limit": "Ограничение скорости", - "tooltip": "Для использования Github Copilot необходимо сначала войти в Github." + "start_auth": "Начать авторизацию", + "step_authorize": "Открыть страницу авторизации", + "step_authorize_desc": "Завершить авторизацию на GitHub", + "step_authorize_detail": "Нажмите кнопку ниже, чтобы открыть страницу авторизации GitHub, затем введите скопированный код авторизации", + "step_connect": "Завершить подключение", + "step_connect_desc": "Подтвердить подключение к GitHub", + "step_connect_detail": "После завершения авторизации на странице GitHub нажмите эту кнопку, чтобы завершить подключение", + "step_copy_code": "Скопировать код авторизации", + "step_copy_code_desc": "Скопировать код авторизации устройства", + "step_copy_code_detail": "Код авторизации автоматически скопирован, вы также можете скопировать его вручную", + "step_get_code": "Получить код авторизации", + "step_get_code_desc": "Сгенерировать код авторизации устройства" }, "delete.content": "Вы уверены, что хотите удалить этот провайдер?", "delete.title": "Удалить провайдер", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 31ad5cbf24..b2f20a657b 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -383,6 +383,7 @@ "confirm": "确认", "copied": "已复制", "copy": "复制", + "copy_failed": "复制失败", "cut": "剪切", "default": "默认", "delete": "删除", @@ -2072,14 +2073,14 @@ "auth_failed": "Github Copilot 认证失败", "auth_success": "Github Copilot 认证成功", "auth_success_title": "认证成功", + "code_copied": "授权码已自动复制到剪贴板", "code_failed": "获取 Device Code 失败,请重试", "code_generated_desc": "请将 Device Code 复制到下面的浏览器链接中", "code_generated_title": "获取 Device Code", - "confirm_login": "过度使用可能会导致您的 Github 账号遭到封号,请谨慎使用!", - "confirm_title": "风险警告", "connect": "连接 Github", "custom_headers": "自定义请求头", "description": "您的 Github 账号需要订阅 Copilot", + "description_detail": "GitHub Copilot 是一个基于 AI 的代码助手,需要有效的 GitHub Copilot 订阅才能使用", "expand": "展开", "headers_description": "自定义请求头 (json 格式)", "invalid_json": "JSON 格式错误", @@ -2089,8 +2090,20 @@ "logout_success": "已成功退出", "model_setting": "模型设置", "open_verification_first": "请先点击上方链接访问验证页面", + "open_verification_page": "打开授权页面", "rate_limit": "速率限制", - "tooltip": "使用 Github Copilot 需要先登录 Github" + "start_auth": "开始授权", + "step_authorize": "打开授权页面", + "step_authorize_desc": "在 GitHub 上完成授权", + "step_authorize_detail": "点击下方按钮打开 GitHub 授权页面,然后输入复制的授权码", + "step_connect": "完成连接", + "step_connect_desc": "确认连接到 GitHub", + "step_connect_detail": "在 GitHub 页面完成授权后,点击此按钮完成连接", + "step_copy_code": "复制授权码", + "step_copy_code_desc": "复制设备授权码", + "step_copy_code_detail": "授权码已自动复制,您也可以手动复制", + "step_get_code": "获取授权码", + "step_get_code_desc": "生成设备授权码" }, "delete.content": "确定要删除此模型提供商吗?", "delete.title": "删除提供商", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 8e1dae06da..6269ad55a6 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -383,6 +383,7 @@ "confirm": "確認", "copied": "已複製", "copy": "複製", + "copy_failed": "複製失敗", "cut": "剪下", "default": "預設", "delete": "刪除", @@ -2072,14 +2073,14 @@ "auth_failed": "Github Copilot 認證失敗", "auth_success": "Github Copilot 認證成功", "auth_success_title": "認證成功", + "code_copied": "授權碼已自動複製到剪貼簿", "code_failed": "獲取 Device Code 失敗,請重試", "code_generated_desc": "請將設備代碼複製到下面的瀏覽器連結中", "code_generated_title": "獲取設備代碼", - "confirm_login": "過度使用可能會導致您的 Github 帳號遭到封號,請謹慎使用!", - "confirm_title": "風險警告", "connect": "連接 Github", "custom_headers": "自訂請求標頭", "description": "您的 Github 帳號需要訂閱 Copilot", + "description_detail": "GitHub Copilot 是一個基於 AI 的程式碼助手,需要有效的 GitHub Copilot 訂閱才能使用", "expand": "展開", "headers_description": "自訂請求標頭 (json 格式)", "invalid_json": "JSON 格式錯誤", @@ -2089,8 +2090,20 @@ "logout_success": "已成功登出", "model_setting": "模型設定", "open_verification_first": "請先點擊上方連結訪問驗證頁面", + "open_verification_page": "開啟授權頁面", "rate_limit": "速率限制", - "tooltip": "使用 Github Copilot 需要先登入 Github" + "start_auth": "開始授權", + "step_authorize": "開啟授權頁面", + "step_authorize_desc": "在 GitHub 上完成授權", + "step_authorize_detail": "點擊下方按鈕開啟 GitHub 授權頁面,然後輸入複製的授權碼", + "step_connect": "完成連線", + "step_connect_desc": "確認連接到 GitHub", + "step_connect_detail": "在 GitHub 頁面完成授權後,點擊此按鈕完成連線", + "step_copy_code": "複製授權碼", + "step_copy_code_desc": "複製設備授權碼", + "step_copy_code_detail": "授權碼已自動複製,您也可以手動複製", + "step_get_code": "獲取授權碼", + "step_get_code_desc": "生成設備授權碼" }, "delete.content": "確定要刪除此提供者嗎?", "delete.title": "刪除提供者", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 6f8f88702d..1c58bd1607 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -1462,8 +1462,6 @@ "code_failed": "Η λήψη του Device Code απέτυχε, παρακαλώ δοκιμάστε ξανά", "code_generated_desc": "Παρακαλώ αντιγράψτε το Device Code στον παρακάτω σύνδεσμο περιηγητή", "code_generated_title": "Λήψη Device Code", - "confirm_login": "Η υπερβολική χρήση μπορεί να οδηγήσει στην απενεργοποίηση του λογαριασμού σας στο Github, παρακαλώ χρησιμοποιήστε το με προσοχή!!!!", - "confirm_title": "Ειδοποίηση κινδύνου", "connect": "Σύνδεση με το Github", "custom_headers": "Προσαρμοσμένες κεφαλίδες αιτήματος", "description": "Ο λογαριασμός σας στο Github χρειάζεται να εγγραφεί για να χρησιμοποιήσει το Copilot", @@ -1476,8 +1474,7 @@ "logout_success": "Έγινε επιτυχής η αποσύνδεση", "model_setting": "Ρυθμίσεις μοντέλου", "open_verification_first": "Παρακαλώ κάντε κλικ στον παραπάνω σύνδεσμο για να επισκεφτείτε τη σελίδα επιβεβαίωσης", - "rate_limit": "Όριο ρυθμού", - "tooltip": "Για τη χρήση του Github Copilot πρέπει να συνδεθείτε στο Github" + "rate_limit": "Όριο ρυθμού" }, "delete.content": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτόν τον παροχό;", "delete.title": "Διαγραφή παρόχου", diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 427ad6b17b..cc81855d93 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -1461,8 +1461,6 @@ "code_failed": "Error al obtener Código del Dispositivo, por favor inténtelo de nuevo", "code_generated_desc": "Por favor, copie el Código del Dispositivo en el siguiente enlace del navegador", "code_generated_title": "Obtener Código del Dispositivo", - "confirm_login": "El uso excesivo puede llevar al bloqueo de su cuenta de Github, use con precaución!!!!", - "confirm_title": "Advertencia de Riesgo", "connect": "Conectar con Github", "custom_headers": "Encabezados personalizados", "description": "Su cuenta de Github necesita suscribirse a Copilot", @@ -1475,8 +1473,7 @@ "logout_success": "Ha cerrado sesión exitosamente", "model_setting": "Configuración del modelo", "open_verification_first": "Por favor, haga clic en el enlace superior para acceder a la página de verificación", - "rate_limit": "Límite de tasa", - "tooltip": "Para usar Github Copilot, primero debe iniciar sesión en Github" + "rate_limit": "Límite de tasa" }, "delete.content": "¿Está seguro de que desea eliminar este proveedor de modelos?", "delete.title": "Eliminar proveedor", diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index eb3070359f..7a586a0b42 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -1462,8 +1462,6 @@ "code_failed": "Échec de l'obtention du code Device, veuillez réessayer", "code_generated_desc": "Veuillez copier le code Device dans le lien du navigateur ci-dessous", "code_generated_title": "Obtenir le code Device", - "confirm_login": "L'utilisation excessive peut entraîner la suspension de votre compte Github, utilisez avec prudence!!!!", - "confirm_title": "Avertissement de risque", "connect": "Connectez-vous à Github", "custom_headers": "Entêtes de requête personnalisées", "description": "Votre compte Github doit souscrire à Copilot", @@ -1476,8 +1474,7 @@ "logout_success": "Déconnexion réussie", "model_setting": "Paramètres du modèle", "open_verification_first": "Cliquez d'abord sur le lien ci-dessus pour accéder à la page de vérification", - "rate_limit": "Limite de taux", - "tooltip": "Pour utiliser Github Copilot, vous devez vous connecter à Github" + "rate_limit": "Limite de taux" }, "delete.content": "Êtes-vous sûr de vouloir supprimer ce fournisseur de modèles ?", "delete.title": "Supprimer le fournisseur", diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 3e7d1adf79..e5ffdc0c7b 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -1463,8 +1463,6 @@ "code_failed": "Falha ao obter Código do Dispositivo, tente novamente", "code_generated_desc": "Por favor, copie o Código do Dispositivo para o link do navegador abaixo", "code_generated_title": "Obter Código do Dispositivo", - "confirm_login": "O uso excessivo pode resultar no bloqueio da sua conta do Github, use com cuidado!!!!", - "confirm_title": "Aviso de Risco", "connect": "Conectar ao Github", "custom_headers": "Cabeçalhos Personalizados", "description": "Sua conta do Github precisa assinar o Copilot", @@ -1477,8 +1475,7 @@ "logout_success": "Saiu com sucesso", "model_setting": "Configuração do Modelo", "open_verification_first": "Por favor, clique no link acima para acessar a página de verificação", - "rate_limit": "Limite de Taxa", - "tooltip": "Para usar o Github Copilot, você precisa fazer login no Github" + "rate_limit": "Limite de Taxa" }, "delete.content": "Tem certeza de que deseja excluir este fornecedor de modelo?", "delete.title": "Excluir Fornecedor", diff --git a/src/renderer/src/pages/settings/ProviderSettings/CustomHeaderPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/CustomHeaderPopup.tsx index 79a872ef7e..54a3b7c822 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/CustomHeaderPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/CustomHeaderPopup.tsx @@ -1,5 +1,6 @@ import CodeEditor from '@renderer/components/CodeEditor' import { TopView } from '@renderer/components/TopView' +import { useCopilot } from '@renderer/hooks/useCopilot' import { useProvider } from '@renderer/hooks/useProvider' import { Provider } from '@renderer/types' import { Modal, Space } from 'antd' @@ -20,17 +21,30 @@ const PopupContainer: React.FC = ({ provider, resolve }) => { const [open, setOpen] = useState(true) const { t } = useTranslation() const { updateProvider } = useProvider(provider.id) - const [headerText, setHeaderText] = useState(JSON.stringify(provider.extra_headers || {}, null, 2)) + const { defaultHeaders, updateDefaultHeaders } = useCopilot() + + const headers = + provider.id === 'copilot' + ? JSON.stringify(defaultHeaders || {}, null, 2) + : JSON.stringify(provider.extra_headers || {}, null, 2) + + const [headerText, setHeaderText] = useState(headers) const onUpdateHeaders = useCallback(() => { try { const headers = headerText.trim() ? JSON.parse(headerText) : {} - updateProvider({ ...provider, extra_headers: headers }) + + if (provider.id === 'copilot') { + updateDefaultHeaders(headers) + } else { + updateProvider({ ...provider, extra_headers: headers }) + } + window.message.success({ content: t('message.save.success.title') }) } catch (error) { window.message.error({ content: t('settings.provider.copilot.invalid_json') }) } - }, [headerText, provider, updateProvider, t]) + }, [headerText, provider, t, updateDefaultHeaders, updateProvider]) const onOk = () => { onUpdateHeaders() diff --git a/src/renderer/src/pages/settings/ProviderSettings/GithubCopilotSettings.tsx b/src/renderer/src/pages/settings/ProviderSettings/GithubCopilotSettings.tsx index 55bb643653..3a79a328a1 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/GithubCopilotSettings.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/GithubCopilotSettings.tsx @@ -1,12 +1,12 @@ import { CheckCircleOutlined, CopyOutlined, ExclamationCircleOutlined } from '@ant-design/icons' import { useCopilot } from '@renderer/hooks/useCopilot' import { useProvider } from '@renderer/hooks/useProvider' -import { Alert, Button, Input, message, Popconfirm, Slider, Space, Tooltip, Typography } from 'antd' +import { Alert, Button, Input, Slider, Steps, Tooltip, Typography } from 'antd' import { FC, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' -import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingTitle } from '..' +import { SettingRow, SettingSubtitle } from '..' interface GithubCopilotSettingsProps { providerId: string @@ -21,27 +21,29 @@ enum AuthStatus { const GithubCopilotSettings: FC = ({ providerId }) => { const { t } = useTranslation() const { provider, updateProvider } = useProvider(providerId) - const { username, avatar, defaultHeaders, updateState, updateDefaultHeaders } = useCopilot() + const { username, avatar, defaultHeaders, updateState } = useCopilot() // 状态管理 const [authStatus, setAuthStatus] = useState(AuthStatus.NOT_STARTED) const [deviceCode, setDeviceCode] = useState('') const [userCode, setUserCode] = useState('') const [verificationUri, setVerificationUri] = useState('') const [loading, setLoading] = useState(false) - const [showHeadersForm, setShowHeadersForm] = useState(false) - const [headerText, setHeaderText] = useState(JSON.stringify(defaultHeaders || {}, null, 2)) const [verificationPageOpened, setVerificationPageOpened] = useState(false) + const [currentStep, setCurrentStep] = useState(0) // 初始化及同步状态 useEffect(() => { if (provider.isAuthed) { setAuthStatus(AuthStatus.AUTHENTICATED) + setCurrentStep(3) } else { setAuthStatus(AuthStatus.NOT_STARTED) + setCurrentStep(0) // 重置其他状态 setDeviceCode('') setUserCode('') setVerificationUri('') + setVerificationPageOpened(false) } }, [provider]) @@ -49,15 +51,27 @@ const GithubCopilotSettings: FC = ({ providerId }) = const handleGetDeviceCode = useCallback(async () => { try { setLoading(true) + setCurrentStep(1) const { device_code, user_code, verification_uri } = await window.api.copilot.getAuthMessage(defaultHeaders) - + console.log('device_code', device_code) + console.log('user_code', user_code) + console.log('verification_uri', verification_uri) setDeviceCode(device_code) setUserCode(user_code) setVerificationUri(verification_uri) setAuthStatus(AuthStatus.CODE_GENERATED) + + // 自动复制授权码到剪贴板 + try { + await navigator.clipboard.writeText(user_code) + window.message.success(t('settings.provider.copilot.code_copied')) + } catch (error) { + console.error('Failed to copy to clipboard:', error) + } } catch (error) { console.error('Failed to get device code:', error) - message.error(t('settings.provider.copilot.code_failed')) + window.message.error(t('settings.provider.copilot.code_failed')) + setCurrentStep(0) } finally { setLoading(false) } @@ -67,6 +81,7 @@ const GithubCopilotSettings: FC = ({ providerId }) = const handleGetToken = useCallback(async () => { try { setLoading(true) + setCurrentStep(3) const { access_token } = await window.api.copilot.getCopilotToken(deviceCode, defaultHeaders) await window.api.copilot.saveCopilotToken(access_token) @@ -77,11 +92,12 @@ const GithubCopilotSettings: FC = ({ providerId }) = setAuthStatus(AuthStatus.AUTHENTICATED) updateState({ username: login, avatar: avatar }) updateProvider({ ...provider, apiKey: token, isAuthed: true }) - message.success(t('settings.provider.copilot.auth_success')) + window.message.success(t('settings.provider.copilot.auth_success')) } } catch (error) { console.error('Failed to get token:', error) - message.error(t('settings.provider.copilot.auth_failed')) + window.message.error(t('settings.provider.copilot.auth_failed')) + setCurrentStep(2) } finally { setLoading(false) } @@ -103,11 +119,13 @@ const GithubCopilotSettings: FC = ({ providerId }) = setDeviceCode('') setUserCode('') setVerificationUri('') + setVerificationPageOpened(false) + setCurrentStep(0) - message.success(t('settings.provider.copilot.logout_success')) + window.message.success(t('settings.provider.copilot.logout_success')) } catch (error) { console.error('Failed to logout:', error) - message.error(t('settings.provider.copilot.logout_failed')) + window.message.error(t('settings.provider.copilot.logout_failed')) // 如果登出失败,重置登出状态 updateProvider({ ...provider, apiKey: '', isAuthed: false }) } finally { @@ -116,9 +134,14 @@ const GithubCopilotSettings: FC = ({ providerId }) = }, [t, updateProvider, provider]) // 复制用户代码 - const handleCopyUserCode = useCallback(() => { - navigator.clipboard.writeText(userCode) - message.success(t('common.copied')) + const handleCopyUserCode = useCallback(async () => { + try { + await navigator.clipboard.writeText(userCode) + window.message.success(t('common.copied')) + } catch (error) { + console.error('Failed to copy to clipboard:', error) + window.message.error(t('common.copy_failed')) + } }, [userCode, t]) // 打开验证页面 @@ -126,27 +149,52 @@ const GithubCopilotSettings: FC = ({ providerId }) = if (verificationUri) { window.open(verificationUri, '_blank') setVerificationPageOpened(true) + setCurrentStep(2) } }, [verificationUri]) - // 处理更新请求头 - const handleUpdateHeaders = useCallback(() => { - try { - // 处理headerText可能为空的情况 - const headers = headerText.trim() ? JSON.parse(headerText) : {} - updateDefaultHeaders(headers) - message.success(t('message.save.success.title')) - } catch (error) { - message.error(t('settings.provider.copilot.invalid_json')) + // 步骤配置 + const getSteps = () => [ + { + title: t('settings.provider.copilot.step_get_code'), + description: t('settings.provider.copilot.step_get_code_desc'), + status: (currentStep > 0 ? 'finish' : currentStep === 0 ? 'process' : 'wait') as + | 'error' + | 'finish' + | 'process' + | 'wait' + }, + { + title: t('settings.provider.copilot.step_copy_code'), + description: t('settings.provider.copilot.step_copy_code_desc'), + status: (currentStep > 1 ? 'finish' : currentStep === 1 ? 'process' : 'wait') as + | 'error' + | 'finish' + | 'process' + | 'wait' + }, + { + title: t('settings.provider.copilot.step_authorize'), + description: t('settings.provider.copilot.step_authorize_desc'), + status: (currentStep > 2 ? 'finish' : currentStep === 2 ? 'process' : 'wait') as + | 'error' + | 'finish' + | 'process' + | 'wait' + }, + { + title: t('settings.provider.copilot.step_connect'), + description: t('settings.provider.copilot.step_connect_desc'), + status: (currentStep >= 3 ? 'finish' : 'wait') as 'error' | 'finish' | 'process' | 'wait' } - }, [headerText, updateDefaultHeaders, t]) + ] // 根据认证状态渲染不同的UI const renderAuthContent = () => { switch (authStatus) { case AuthStatus.AUTHENTICATED: return ( - <> + = ({ providerId }) = icon={} showIcon /> - + ) case AuthStatus.CODE_GENERATED: return ( - <> - -

{t('settings.provider.copilot.code_generated_desc')}

- {verificationUri} - - } - showIcon - /> - - - - - - - - - - + + + + + + + {/* 步骤2: 复制授权码 */} + {currentStep >= 1 && ( + + + 1}>2 +
+ {t('settings.provider.copilot.step_copy_code')} + {t('settings.provider.copilot.step_copy_code_detail')} +
+
+ + + + +
+ )} + + {/* 步骤3: 打开授权页面 */} + {currentStep >= 1 && ( + + + 2}>3 +
+ {t('settings.provider.copilot.step_authorize')} + {t('settings.provider.copilot.step_authorize_detail')} +
+
+ + {verificationUri && ( + + {verificationUri} + + )} +
+ )} + + {/* 步骤4: 完成连接 */} + {currentStep >= 2 && ( + + + 3}>4 +
+ {t('settings.provider.copilot.step_connect')} + {t('settings.provider.copilot.step_connect_detail')} +
+
+ + + +
+ )} +
+
) default: // AuthStatus.NOT_STARTED return ( - <> + + {t('settings.provider.copilot.start_auth')} + + } showIcon + icon={} /> - - }> - - - + ) } } return ( - - {renderAuthContent()} - - - {t('settings.provider.copilot.model_setting')} - - - {t('settings.provider.copilot.rate_limit')} - updateProvider({ ...provider, rateLimit: value })} - /> - - - {t('settings.provider.copilot.custom_headers')} - - - {showHeadersForm && ( - - - {t('settings.provider.copilot.headers_description')} - setHeaderText(e.target.value)} - placeholder={`{\n "Header-Name": "Header-Value"\n}`} - /> - - - - - - - )} - - + {renderAuthContent()} + {authStatus === AuthStatus.AUTHENTICATED && ( + + {t('settings.provider.copilot.rate_limit')} + updateProvider({ ...provider, rateLimit: value })} + /> + + )} ) } -const Container = styled.div`` +const Container = styled.div` + padding-top: 15px; +` + +const StartContainer = styled.div` + margin-bottom: 20px; +` + +const AuthSuccessContainer = styled.div` + margin-bottom: 20px; +` + +const AuthFlowContainer = styled.div` + display: flex; + gap: 24px; + margin-bottom: 20px; + + @media (max-width: 768px) { + flex-direction: column; + gap: 16px; + } +` + +const StepsContainer = styled.div` + flex: 1; + min-width: 200px; + + .ant-steps-item-description { + margin-top: 4px; + font-size: 12px; + color: var(--color-text-secondary); + } +` + +const AuthActionsContainer = styled.div` + flex: 2; + display: flex; + flex-direction: column; + gap: 16px; +` + +const StepCard = styled.div` + padding: 16px; + border: 1px solid var(--color-border); + border-radius: 8px; + background: var(--color-background-soft); + transition: all 0.2s ease; + + &:hover { + border-color: var(--color-border-soft); + } +` + +const StepHeader = styled.div` + display: flex; + align-items: flex-start; + gap: 12px; + margin-bottom: 12px; +` + +const StepNumber = styled.div<{ completed?: boolean }>` + width: 24px; + height: 24px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: bold; + background: ${(props) => (props.completed ? 'var(--color-status-success)' : 'var(--color-primary)')}; + color: white; + flex-shrink: 0; + transition: all 0.2s ease; +` + +const StepTitle = styled.div` + font-weight: 500; + font-size: 14px; + color: var(--color-text); +` + +const StepDesc = styled.div` + font-size: 12px; + color: var(--color-text-secondary); + margin-top: 2px; +` export default GithubCopilotSettings diff --git a/src/renderer/src/pages/settings/ProviderSettings/ModelList.tsx b/src/renderer/src/pages/settings/ProviderSettings/ModelList.tsx index c032467a66..b9df9e699c 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ModelList.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ModelList.tsx @@ -308,7 +308,7 @@ const ModelList: React.FC = ({ providerId, modelStatuses = [], s ))} - {(docsWebsite || modelsWebsite) && ( + {docsWebsite || modelsWebsite ? ( {t('settings.provider.docs_check')} {docsWebsite && ( @@ -325,6 +325,8 @@ const ModelList: React.FC = ({ providerId, modelStatuses = [], s )} {t('settings.provider.docs_more_details')} + ) : ( +
)}