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' && (
-
-
- )}
+
+
-
- {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 */}
+
)
}
@@ -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()