diff --git a/packages/shared/config/types.ts b/packages/shared/config/types.ts index 6d76173e26..5c42f1d2b2 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 f812517179..5a4b77f651 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' @@ -202,12 +203,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, @@ -216,13 +212,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 06cc53ec12..2e86111da1 100644 --- a/src/renderer/src/i18n/translate/de-de.json +++ b/src/renderer/src/i18n/translate/de-de.json @@ -2923,15 +2923,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 fe7885030b..a793d9ddf0 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -2923,15 +2923,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 182da4c1a0..27b46ce3b3 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -2923,15 +2923,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 32ee893d1a..b710e40b1e 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -2923,15 +2923,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 fbbbbc9048..449e6bad29 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -2923,15 +2923,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 aa185cb07d..5ac0f082c1 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -2923,15 +2923,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 8df5ef6630..64a6453115 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -2923,15 +2923,14 @@ }, "description": "Мощный AI-ассистент для созидания", "downloading": "Загрузка...", + "enterprise": { + "title": "Предприятие" + }, "feedback": { "button": "Обратная связь", "title": "Обратная связь" }, "label": "О программе и обратная связь", - "license": { - "button": "Лицензия", - "title": "Лицензия" - }, "releases": { "button": "Релизы", "title": "Заметки о релизах" @@ -3043,7 +3042,7 @@ "confirm": { "button": "Выберите файл резервной копии" }, - "content": "Экспорт части данных, включая чат и настройки. Пожалуйста, обратите внимание, что процесс резервного копирования может занять некоторое время. Благодарим за ваше терпение.", + "content": "Экспорт части данных, включая историю чатов и настройки. Обратите внимание, процесс резервного копирования может занять некоторое время, благодарим за ваше терпение.", "lan": { "auto_close_tip": "Автоматическое закрытие через {{seconds}} секунд...", "confirm_close_message": "Передача файла в процессе. Закрытие прервет передачу. Вы уверены, что хотите принудительно закрыть?",