diff --git a/electron.vite.config.ts b/electron.vite.config.ts index ff9a0c9e92..35cf21a6f0 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -54,7 +54,18 @@ export default defineConfig({ } }, build: { - sourcemap: isDev + sourcemap: isDev, + rollupOptions: { + // Unlike renderer which auto-discovers entries from HTML files, + // preload requires explicit entry point configuration for multiple scripts + input: { + index: resolve(__dirname, 'src/preload/index.ts'), + simplest: resolve(__dirname, 'src/preload/simplest.ts') // Minimal preload + }, + output: { + entryFileNames: '[name].js' + } + } } }, renderer: { diff --git a/src/main/data/migrate/dataRefactor/DataRefactorMigrateService.ts b/src/main/data/migrate/dataRefactor/DataRefactorMigrateService.ts index dfc7aa432c..16695c89f2 100644 --- a/src/main/data/migrate/dataRefactor/DataRefactorMigrateService.ts +++ b/src/main/data/migrate/dataRefactor/DataRefactorMigrateService.ts @@ -375,16 +375,16 @@ class DataRefactorMigrateService { this.registerMigrationIpcHandlers() this.migrateWindow = new BrowserWindow({ - width: 800, - height: 650, - resizable: true, - maximizable: true, - minimizable: true, + width: 640, + height: 480, + resizable: false, + maximizable: false, + minimizable: false, show: false, + frame: false, autoHideMenuBar: true, - titleBarStyle: 'default', webPreferences: { - preload: join(__dirname, '../preload/index.js'), + preload: join(__dirname, '../preload/simplest.js'), sandbox: false, webSecurity: false, contextIsolation: true @@ -400,9 +400,6 @@ class DataRefactorMigrateService { this.migrateWindow.once('ready-to-show', () => { this.migrateWindow?.show() - if (!app.isPackaged) { - this.migrateWindow?.webContents.openDevTools() - } }) this.migrateWindow.on('closed', () => { diff --git a/src/preload/simplest.ts b/src/preload/simplest.ts new file mode 100644 index 0000000000..785bdbca8f --- /dev/null +++ b/src/preload/simplest.ts @@ -0,0 +1,17 @@ +import { electronAPI } from '@electron-toolkit/preload' +import { contextBridge } from 'electron' + +// Use `contextBridge` APIs to expose Electron APIs to +// renderer only if context isolation is enabled, otherwise +// just add to the DOM global. +if (process.contextIsolated) { + try { + contextBridge.exposeInMainWorld('electron', electronAPI) + } catch (error) { + // eslint-disable-next-line no-restricted-syntax + console.error('[Preload]Failed to expose APIs:', error as Error) + } +} else { + // @ts-ignore (define in dts) + window.electron = electronAPI +} diff --git a/src/renderer/dataRefactorMigrate.html b/src/renderer/dataRefactorMigrate.html index 4a23d3d7c7..93e2bfbec1 100644 --- a/src/renderer/dataRefactorMigrate.html +++ b/src/renderer/dataRefactorMigrate.html @@ -9,8 +9,54 @@ - + + \ No newline at end of file diff --git a/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx b/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx index 1b9bec4973..3e93807d30 100644 --- a/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx +++ b/src/renderer/src/windows/dataRefactorMigrate/MigrateApp.tsx @@ -1,11 +1,10 @@ -import { CheckCircleOutlined, CloudUploadOutlined, LoadingOutlined, RocketOutlined } from '@ant-design/icons' +import { AppLogo } from '@renderer/config/env' import { IpcChannel } from '@shared/IpcChannel' -import { Alert, Button, Card, Progress, Space, Steps, Typography } from 'antd' +import { Button, Progress, Space, Steps } from 'antd' +import { AlertTriangle, CheckCircle, Database, Loader2, Rocket } from 'lucide-react' import React, { useEffect, useMemo, useState } from 'react' import styled from 'styled-components' -const { Title, Text } = Typography - type MigrationStage = | 'introduction' // Introduction phase - user can cancel | 'backup_required' // Backup required - show backup requirement @@ -116,33 +115,37 @@ const MigrateApp: React.FC = () => { const getProgressColor = () => { switch (progress.stage) { case 'completed': - return '#52c41a' + return 'var(--color-primary)' case 'error': return '#ff4d4f' case 'backup_confirmed': - return '#52c41a' + return 'var(--color-primary)' default: - return '#1890ff' + return 'var(--color-primary)' } } const getCurrentStepIcon = () => { switch (progress.stage) { case 'introduction': - return + return case 'backup_required': case 'backup_progress': - return + return case 'backup_confirmed': - return + return case 'migration': - return + return ( + + + + ) case 'completed': - return + return case 'error': - return
⚠️
+ return default: - return + return } } @@ -150,48 +153,62 @@ const MigrateApp: React.FC = () => { switch (progress.stage) { case 'introduction': return ( - + <> + - + ) case 'backup_required': return ( - + <> + + - - + ) case 'backup_confirmed': return ( - + - - + + + + ) case 'migration': - return + return ( + +
+ +
+ ) case 'completed': return ( - + +
+ +
) case 'error': return ( - + - - + + + + ) default: return null @@ -200,94 +217,109 @@ const MigrateApp: React.FC = () => { return ( - - - 数据迁移向导 - - - } - bordered={false}> -
- -
+ {/* Header */} +
+ -
{getCurrentStepIcon()}
+ 数据迁移向导 +
- {progress.stage !== 'introduction' && progress.stage !== 'error' && ( - - + {/* Left Sidebar with Steps */} + + + - - )} + + - - {progress.message} - + {/* Right Content Area */} + + + {getCurrentStepIcon()} - {progress.stage === 'introduction' && ( - - )} + {progress.stage === 'introduction' && ( + + 将数据迁移到新的架构中 + + Cherry Studio对数据的存储和使用方式进行了重大重构,在新的架构下,效率和安全性将会得到极大提升。 +
+
+ 数据必须进行迁移,才能在新版本中使用。 +
+
+ 我们会指导你完成迁移,迁移过程不会损坏原来的数据,你随时可以取消迁移,并继续使用旧版本。 +
+
+ )} - {progress.stage === 'backup_required' && ( - - )} + {progress.stage === 'backup_required' && ( + + 创建数据备份 + + 迁移前必须创建数据备份以确保数据安全。请选择备份位置或确认已有最新备份。 + + + )} - {progress.stage === 'backup_confirmed' && ( - - )} + {progress.stage === 'backup_progress' && ( + + 准备数据备份 + 请选择备份位置,保存后等待备份完成。 + + )} - {progress.stage === 'error' && ( - - )} + {progress.stage === 'backup_confirmed' && ( + + 备份完成 + + 数据备份已完成,现在可以安全地开始迁移。 + + + )} - {progress.stage === 'completed' && ( - - )} + {progress.stage === 'error' && ( + + 迁移失败 + + {progress.error || '迁移过程遇到错误,您可以重新尝试或继续使用之前版本(原始数据完好保存)。'} + + + )} -
{renderActionButtons()}
-
+ {progress.stage === 'completed' && ( + + 迁移完成 + 数据已成功迁移,重启应用后即可正常使用。 + + )} + + {progress.stage !== 'introduction' && + progress.stage !== 'error' && + progress.stage !== 'backup_required' && + progress.stage !== 'backup_confirmed' && ( + + + + )} + + + + + {/* Footer */} +
{renderActionButtons()}
) } @@ -297,36 +329,163 @@ const Container = styled.div` height: 100vh; display: flex; flex-direction: column; - justify-content: center; - align-items: center; - background: #f5f5f5; - padding: 20px; + background: #fff; ` -const MigrationCard = styled(Card)` +const Header = styled.div` + height: 48px; + background: rgb(240, 240, 240); + display: flex; + align-items: center; + justify-content: center; + z-index: 10; + -webkit-app-region: drag; + user-select: none; +` + +const HeaderTitle = styled.div` + font-size: 16px; + font-weight: 600; + color: black; + margin-left: 12px; +` + +const HeaderLogo = styled.img` + width: 24px; + height: 24px; + border-radius: 6px; +` + +const MainContent = styled.div` + flex: 1; + display: flex; + overflow: hidden; +` + +const LeftSidebar = styled.div` + width: 150px; + background: #fff; + border-right: 1px solid #f0f0f0; + display: flex; + flex-direction: column; +` + +const StepsContainer = styled.div` + padding: 32px 24px; + flex: 1; + + .ant-steps-item-process .ant-steps-item-icon { + background-color: var(--color-primary); + border-color: var(--color-primary-soft); + } + + .ant-steps-item-finish .ant-steps-item-icon { + background-color: var(--color-primary-mute); + border-color: var(--color-primary-mute); + } + + .ant-steps-item-finish .ant-steps-item-icon > .ant-steps-icon { + color: var(--color-primary); + } + + .ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon { + color: #fff; + } + + .ant-steps-item-wait .ant-steps-item-icon { + border-color: #d9d9d9; + } +` + +const RightContent = styled.div` + flex: 1; + display: flex; + flex-direction: column; +` + +const ContentArea = styled.div` + flex: 1; + display: flex; + flex-direction: column; + /* justify-content: center; */ + /* align-items: center; */ + /* margin: 0 auto; */ width: 100%; - max-width: 600px; - border-radius: 12px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + padding: 24px; +` - .ant-card-head { - background: #fff; - border-bottom: 1px solid #f0f0f0; - } +const Footer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; - .ant-card-body { - padding: 24px 32px 32px 32px; - } + background: rgb(250, 250, 250); + + height: 64px; + + padding: 0 24px; + gap: 16px; +` + +const Spacer = styled.div` + flex: 1; ` const ProgressContainer = styled.div` - margin: 24px 0; + margin: 32px 0; + width: 100%; ` -const MessageContainer = styled.div` +const ButtonRow = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + min-width: 300px; +` + +const InfoIcon = styled.div` + display: flex; + justify-content: center; + align-items: center; + margin-top: 12px; +` + +const InfoCard = styled.div<{ variant?: 'info' | 'warning' | 'success' | 'error' }>` + width: 100%; +` + +const InfoTitle = styled.div` + margin-bottom: 32px; + margin-top: 32px; + font-size: 16px; + font-weight: 600; + color: var(--color-primary); + line-height: 1.4; text-align: center; - margin: 16px 0; - min-height: 24px; +` + +const InfoDescription = styled.p` + margin: 0; + color: rgba(0, 0, 0, 0.68); + line-height: 1.8; + max-width: 420px; + margin: 0 auto; +` + +const SpinningIcon = styled.div` + display: inline-block; + animation: spin 2s linear infinite; + + @keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } ` export default MigrateApp diff --git a/src/renderer/src/windows/dataRefactorMigrate/entryPoint.tsx b/src/renderer/src/windows/dataRefactorMigrate/entryPoint.tsx index 67ce227a54..c5cd7a55ea 100644 --- a/src/renderer/src/windows/dataRefactorMigrate/entryPoint.tsx +++ b/src/renderer/src/windows/dataRefactorMigrate/entryPoint.tsx @@ -1,9 +1,10 @@ -import '../../assets/styles/index.scss' +import '@ant-design/v5-patch-for-react-19' +import '@renderer/assets/styles/index.scss' -import ReactDOM from 'react-dom/client' +import { createRoot } from 'react-dom/client' import MigrateApp from './MigrateApp' -const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) +const root = createRoot(document.getElementById('root') as HTMLElement) root.render()