diff --git a/packages/shared/config/types.ts b/packages/shared/config/types.ts index 8012ed9022..d533431c93 100644 --- a/packages/shared/config/types.ts +++ b/packages/shared/config/types.ts @@ -31,3 +31,16 @@ export type WebviewKeyEvent = { shift: boolean alt: boolean } + +export interface WebSocketStatusResponse { + isRunning: boolean + port?: number + ip?: string + clientConnected: boolean +} + +export interface WebSocketCandidatesResponse { + host: string + interface: string + priority: number +} diff --git a/src/main/services/WebSocketService.ts b/src/main/services/WebSocketService.ts index f1cbe8f426..4cdcfafee5 100644 --- a/src/main/services/WebSocketService.ts +++ b/src/main/services/WebSocketService.ts @@ -1,4 +1,5 @@ import { loggerService } from '@logger' +import { WebSocketCandidatesResponse, WebSocketStatusResponse } from '@shared/config/types' import * as fs from 'fs' import { networkInterfaces } from 'os' import * as path from 'path' @@ -201,12 +202,7 @@ class WebSocketService { } } - public getStatus = async (): Promise<{ - isRunning: boolean - port?: number - ip?: string - clientConnected: boolean - }> => { + public getStatus = async (): Promise => { return { isRunning: this.isStarted, port: this.isStarted ? this.port : undefined, @@ -215,13 +211,7 @@ class WebSocketService { } } - public getAllCandidates = async (): Promise< - Array<{ - host: string - interface: string - priority: number - }> - > => { + public getAllCandidates = async (): Promise => { const interfaces = networkInterfaces() // 按优先级排序的网络接口名称模式 diff --git a/src/renderer/src/components/Popups/ExportToPhoneLanPopup.tsx b/src/renderer/src/components/Popups/ExportToPhoneLanPopup.tsx index d826ddd01d..26caf29d86 100644 --- a/src/renderer/src/components/Popups/ExportToPhoneLanPopup.tsx +++ b/src/renderer/src/components/Popups/ExportToPhoneLanPopup.tsx @@ -3,7 +3,9 @@ import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@herou import { Progress } from '@heroui/progress' import { Spinner } from '@heroui/spinner' import { loggerService } from '@logger' +import { AppLogo } from '@renderer/config/env' import { SettingHelpText, SettingRow } from '@renderer/pages/settings' +import { WebSocketCandidatesResponse } from '@shared/config/types' import { QRCodeSVG } from 'qrcode.react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -38,12 +40,12 @@ const ScanQRCode: React.FC<{ qrCodeValue: string }> = ({ qrCodeValue }) => { @@ -198,17 +200,28 @@ const PopupContainer: React.FC = ({ resolve }) => { const { port, ip } = await window.api.webSocket.status() if (ip && port) { - const candidates = await window.api.webSocket.getAllCandidates() - const connectionInfo = { - type: 'cherry-studio-app', - candidates, - selectedHost: ip, - port, - timestamp: Date.now() + const candidatesData = await window.api.webSocket.getAllCandidates() + + const optimizeConnectionInfo = () => { + const ipToNumber = (ip: string) => { + return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) + } + + const compressedData = [ + 'CSA', + ipToNumber(ip), + candidatesData.map((candidate: WebSocketCandidatesResponse) => ipToNumber(candidate.host)), + port, // 端口号 + Date.now() % 86400000 + ] + + return compressedData } - setQrCodeValue(JSON.stringify(connectionInfo)) + + const compressedData = optimizeConnectionInfo() + const qrCodeValue = JSON.stringify(compressedData) + setQrCodeValue(qrCodeValue) setConnectionPhase('waiting_qr_scan') - logger.info(`QR code generated: ${ip}:${port} with ${candidates.length} IP candidates`) } else { setError(t('settings.data.export_to_phone.lan.error.no_ip')) setConnectionPhase('error') diff --git a/src/renderer/src/i18n/translate/de-de.json b/src/renderer/src/i18n/translate/de-de.json index 0119c48ea9..ba2c6bbcc0 100644 --- a/src/renderer/src/i18n/translate/de-de.json +++ b/src/renderer/src/i18n/translate/de-de.json @@ -2795,15 +2795,14 @@ }, "description": "Ein KI-Assistent für Kreative", "downloading": "Update wird heruntergeladen...", + "enterprise": { + "title": "Unternehmen" + }, "feedback": { "button": "Feedback", "title": "Feedback" }, "label": "Über uns", - "license": { - "button": "Anzeigen", - "title": "Lizenz" - }, "releases": { "button": "Anzeigen", "title": "Changelog" diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index c187612387..2e9d02bbcc 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -2584,15 +2584,14 @@ }, "description": "Ένα AI ασιστάντα που έχει σχεδιαστεί για δημιουργούς", "downloading": "Λήψη ενημερώσεων...", + "enterprise": { + "title": "Επιχείρηση" + }, "feedback": { "button": "Σχόλια και Παρατηρήσεις", "title": "Αποστολή σχολίων" }, "label": "Περί μας", - "license": { - "button": "Προβολή", - "title": "Licenses" - }, "releases": { "button": "Προβολή", "title": "Ημερολόγιο Ενημερώσεων" diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index c67d382d06..7e5eb4eebf 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -2584,15 +2584,14 @@ }, "description": "Una asistente de IA creada para los creadores", "downloading": "Descargando actualización...", + "enterprise": { + "title": "Empresa" + }, "feedback": { "button": "Enviar feedback", "title": "Enviar comentarios" }, "label": "Acerca de nosotros", - "license": { - "button": "Ver", - "title": "Licencia" - }, "releases": { "button": "Ver", "title": "Registro de cambios" diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index e9336eeb98..7eb8a1ec37 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -2584,15 +2584,14 @@ }, "description": "Un assistant IA conçu pour les créateurs", "downloading": "Téléchargement de la mise à jour en cours...", + "enterprise": { + "title": "Entreprise" + }, "feedback": { "button": "Faire un retour", "title": "Retour d'information" }, "label": "À propos de nous", - "license": { - "button": "Afficher", - "title": "Licence" - }, "releases": { "button": "Afficher", "title": "Journal des mises à jour" diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 5db0fceb9a..2a957e6b78 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -2584,15 +2584,14 @@ }, "description": "クリエイターのための強力なAIアシスタント", "downloading": "ダウンロード中...", + "enterprise": { + "title": "エンタープライズ" + }, "feedback": { "button": "フィードバック", "title": "フィードバック" }, "label": "について", - "license": { - "button": "ライセンス", - "title": "ライセンス" - }, "releases": { "button": "リリース", "title": "リリースノート" diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index dfb5cf1797..a348719660 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -2584,15 +2584,14 @@ }, "description": "Um assistente de IA criado para criadores", "downloading": "Baixando atualizações...", + "enterprise": { + "title": "Empresa" + }, "feedback": { "button": "Feedback", "title": "Enviar feedback" }, "label": "Sobre Nós", - "license": { - "button": "Ver", - "title": "Licença" - }, "releases": { "button": "Ver", "title": "Registro de alterações" diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 8bae68c5be..9307fc1d95 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -2584,15 +2584,14 @@ }, "description": "Мощный AI-ассистент для созидания", "downloading": "Загрузка...", + "enterprise": { + "title": "Предприятие" + }, "feedback": { "button": "Обратная связь", "title": "Обратная связь" }, "label": "О программе и обратная связь", - "license": { - "button": "Лицензия", - "title": "Лицензия" - }, "releases": { "button": "Релизы", "title": "Заметки о релизах" @@ -2704,7 +2703,7 @@ "confirm": { "button": "[to be translated]:选择备份文件" }, - "content": "[to be translated]:导出部分数据,包括聊天记录、设置。请注意,备份过程可能需要一些时间,感谢您的耐心等待。", + "content": "Экспорт части данных, включая историю чатов и настройки. Обратите внимание, процесс резервного копирования может занять некоторое время, благодарим за ваше терпение.", "lan": { "auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...", "confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",