mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 22:10:21 +08:00
feat: added data folder
This commit is contained in:
parent
6d665fa926
commit
518a3b26b6
@ -2,6 +2,7 @@ import path from 'node:path'
|
|||||||
|
|
||||||
import { ThemeMode } from '@types'
|
import { ThemeMode } from '@types'
|
||||||
import { BrowserWindow, ipcMain, session, shell } from 'electron'
|
import { BrowserWindow, ipcMain, session, shell } from 'electron'
|
||||||
|
import log from 'electron-log'
|
||||||
|
|
||||||
import { titleBarOverlayDark, titleBarOverlayLight } from './config'
|
import { titleBarOverlayDark, titleBarOverlayLight } from './config'
|
||||||
import AppUpdater from './services/AppUpdater'
|
import AppUpdater from './services/AppUpdater'
|
||||||
@ -23,7 +24,9 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
|||||||
version: app.getVersion(),
|
version: app.getVersion(),
|
||||||
isPackaged: app.isPackaged,
|
isPackaged: app.isPackaged,
|
||||||
appPath: app.getAppPath(),
|
appPath: app.getAppPath(),
|
||||||
filesPath: path.join(app.getPath('userData'), 'Data', 'Files')
|
filesPath: path.join(app.getPath('userData'), 'Data', 'Files'),
|
||||||
|
appDataPath: app.getPath('userData'),
|
||||||
|
logsPath: log.transports.file.getFile().path
|
||||||
}))
|
}))
|
||||||
|
|
||||||
ipcMain.handle('app:proxy', async (_, proxy: string) => {
|
ipcMain.handle('app:proxy', async (_, proxy: string) => {
|
||||||
@ -110,4 +113,9 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
|||||||
|
|
||||||
// export
|
// export
|
||||||
ipcMain.handle('export:word', exportService.exportToWord)
|
ipcMain.handle('export:word', exportService.exportToWord)
|
||||||
|
|
||||||
|
// open path
|
||||||
|
ipcMain.handle('open:path', async (_, path: string) => {
|
||||||
|
await shell.openPath(path)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/preload/index.d.ts
vendored
10
src/preload/index.d.ts
vendored
@ -1,7 +1,7 @@
|
|||||||
import { ElectronAPI } from '@electron-toolkit/preload'
|
import { ElectronAPI } from '@electron-toolkit/preload'
|
||||||
import { FileType } from '@renderer/types'
|
import { FileType } from '@renderer/types'
|
||||||
import { WebDavConfig } from '@renderer/types'
|
import { WebDavConfig } from '@renderer/types'
|
||||||
import { LanguageVarious } from '@renderer/types'
|
import { AppInfo, LanguageVarious } from '@renderer/types'
|
||||||
import type { OpenDialogOptions } from 'electron'
|
import type { OpenDialogOptions } from 'electron'
|
||||||
import { Readable } from 'stream'
|
import { Readable } from 'stream'
|
||||||
|
|
||||||
@ -9,12 +9,7 @@ declare global {
|
|||||||
interface Window {
|
interface Window {
|
||||||
electron: ElectronAPI
|
electron: ElectronAPI
|
||||||
api: {
|
api: {
|
||||||
getAppInfo: () => Promise<{
|
getAppInfo: () => Promise<AppInfo>
|
||||||
version: string
|
|
||||||
isPackaged: boolean
|
|
||||||
appPath: string
|
|
||||||
filesPath: string
|
|
||||||
}>
|
|
||||||
checkForUpdate: () => void
|
checkForUpdate: () => void
|
||||||
openWebsite: (url: string) => void
|
openWebsite: (url: string) => void
|
||||||
setProxy: (proxy: string | undefined) => void
|
setProxy: (proxy: string | undefined) => void
|
||||||
@ -57,6 +52,7 @@ declare global {
|
|||||||
export: {
|
export: {
|
||||||
toWord: (markdown: string, fileName: string) => Promise<void>
|
toWord: (markdown: string, fileName: string) => Promise<void>
|
||||||
}
|
}
|
||||||
|
openPath: (path: string) => Promise<void>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,8 @@ const api = {
|
|||||||
},
|
},
|
||||||
export: {
|
export: {
|
||||||
toWord: (markdown: string, fileName: string) => ipcRenderer.invoke('export:word', markdown, fileName)
|
toWord: (markdown: string, fileName: string) => ipcRenderer.invoke('export:word', markdown, fileName)
|
||||||
}
|
},
|
||||||
|
openPath: (path: string) => ipcRenderer.invoke('open:path', path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use `contextBridge` APIs to expose Electron APIs to
|
// Use `contextBridge` APIs to expose Electron APIs to
|
||||||
|
|||||||
@ -348,6 +348,9 @@
|
|||||||
"topic.position.right": "Right",
|
"topic.position.right": "Right",
|
||||||
"topic.show.time": "Show Topic Time",
|
"topic.show.time": "Show Topic Time",
|
||||||
"display.title": "Display Settings",
|
"display.title": "Display Settings",
|
||||||
|
"data.title": "Data Directory",
|
||||||
|
"data.app_data": "App Data",
|
||||||
|
"data.app_logs": "App Logs",
|
||||||
"shortcuts": {
|
"shortcuts": {
|
||||||
"title": "Keyboard Shortcuts",
|
"title": "Keyboard Shortcuts",
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
|
|||||||
@ -348,6 +348,9 @@
|
|||||||
"topic.position.right": "Справа",
|
"topic.position.right": "Справа",
|
||||||
"topic.show.time": "Показывать время топика",
|
"topic.show.time": "Показывать время топика",
|
||||||
"display.title": "Настройки отображения",
|
"display.title": "Настройки отображения",
|
||||||
|
"data.title": "Каталог данных",
|
||||||
|
"data.app_data": "Данные приложения",
|
||||||
|
"data.app_logs": "Логи приложения",
|
||||||
"shortcuts": {
|
"shortcuts": {
|
||||||
"title": "Горячие клавиши",
|
"title": "Горячие клавиши",
|
||||||
"action": "Действие",
|
"action": "Действие",
|
||||||
|
|||||||
@ -336,6 +336,9 @@
|
|||||||
"topic.position.right": "右侧",
|
"topic.position.right": "右侧",
|
||||||
"topic.show.time": "显示话题时间",
|
"topic.show.time": "显示话题时间",
|
||||||
"display.title": "显示设置",
|
"display.title": "显示设置",
|
||||||
|
"data.title": "数据目录",
|
||||||
|
"data.app_data": "应用数据",
|
||||||
|
"data.app_logs": "应用日志",
|
||||||
"shortcuts": {
|
"shortcuts": {
|
||||||
"title": "快捷方式",
|
"title": "快捷方式",
|
||||||
"action": "操作",
|
"action": "操作",
|
||||||
|
|||||||
@ -336,6 +336,9 @@
|
|||||||
"topic.position.right": "右側",
|
"topic.position.right": "右側",
|
||||||
"topic.show.time": "顯示話題時間",
|
"topic.show.time": "顯示話題時間",
|
||||||
"display.title": "顯示設定",
|
"display.title": "顯示設定",
|
||||||
|
"data.title": "數據目錄",
|
||||||
|
"data.app_data": "應用數據",
|
||||||
|
"data.app_logs": "應用日誌",
|
||||||
"shortcuts": {
|
"shortcuts": {
|
||||||
"title": "快速方式",
|
"title": "快速方式",
|
||||||
"action": "操作",
|
"action": "操作",
|
||||||
|
|||||||
@ -1,61 +1,102 @@
|
|||||||
import { FolderOpenOutlined, SaveOutlined } from '@ant-design/icons'
|
import { FileSearchOutlined, FolderOpenOutlined, SaveOutlined } from '@ant-design/icons'
|
||||||
import { HStack, VStack } from '@renderer/components/Layout'
|
import { HStack, VStack } from '@renderer/components/Layout'
|
||||||
import { backup, reset, restore } from '@renderer/services/BackupService'
|
import { backup, reset, restore } from '@renderer/services/BackupService'
|
||||||
import { Button } from 'antd'
|
import { AppInfo } from '@renderer/types'
|
||||||
import { FC } from 'react'
|
import { Button, Typography } from 'antd'
|
||||||
|
import { FC, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Link, Route, Routes } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
import WebDavSettings from './WebDavSettings'
|
import WebDavSettings from './WebDavSettings'
|
||||||
|
|
||||||
const DataSettings: FC = () => {
|
const DataSettings: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const [appInfo, setAppInfo] = useState<AppInfo>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.api.getAppInfo().then(setAppInfo)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleOpenPath = (path: string) => {
|
||||||
|
if (path?.endsWith('log')) {
|
||||||
|
const dirPath = path.split(/[/\\]/).slice(0, -1).join('/')
|
||||||
|
window.api.openPath(dirPath)
|
||||||
|
} else {
|
||||||
|
window.api.openPath(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<SettingContainer>
|
||||||
<Route
|
<SettingGroup>
|
||||||
path="/"
|
<SettingTitle>{t('settings.data')}</SettingTitle>
|
||||||
element={
|
<SettingDivider />
|
||||||
<SettingContainer>
|
<SettingRow>
|
||||||
<SettingTitle>{t('settings.data')}</SettingTitle>
|
<SettingRowTitle>{t('settings.data.webdav.title')}</SettingRowTitle>
|
||||||
<SettingDivider />
|
<VStack gap="5px">
|
||||||
<SettingRow style={{ minHeight: 32 }}>
|
<Link to="/settings/data/webdav" style={{ color: 'var(--color-text-2)' }}>
|
||||||
<SettingRowTitle>{t('settings.data.webdav.title')}</SettingRowTitle>
|
<Button>{t('settings.general.view_webdav_settings')}</Button>
|
||||||
<VStack gap="5px">
|
</Link>
|
||||||
<Link to="/settings/data/webdav" style={{ color: 'var(--color-text-2)' }}>
|
</VStack>
|
||||||
<Button>{t('settings.general.view_webdav_settings')}</Button>
|
</SettingRow>
|
||||||
</Link>
|
<SettingDivider />
|
||||||
</VStack>
|
<SettingRow>
|
||||||
</SettingRow>
|
<SettingRowTitle>{t('settings.general.backup.title')}</SettingRowTitle>
|
||||||
<SettingDivider />
|
<HStack gap="5px" justifyContent="space-between">
|
||||||
<SettingRow>
|
<Button onClick={backup} icon={<SaveOutlined />}>
|
||||||
<SettingRowTitle>{t('settings.general.backup.title')}</SettingRowTitle>
|
{t('settings.general.backup.button')}
|
||||||
<HStack gap="5px" justifyContent="space-between">
|
</Button>
|
||||||
<Button onClick={backup} icon={<SaveOutlined />}>
|
<Button onClick={restore} icon={<FolderOpenOutlined />}>
|
||||||
{t('settings.general.backup.button')}
|
{t('settings.general.restore.button')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={restore} icon={<FolderOpenOutlined />}>
|
</HStack>
|
||||||
{t('settings.general.restore.button')}
|
</SettingRow>
|
||||||
</Button>
|
<SettingDivider />
|
||||||
</HStack>
|
<SettingRow>
|
||||||
</SettingRow>
|
<SettingRowTitle>{t('settings.general.reset.title')}</SettingRowTitle>
|
||||||
<SettingDivider />
|
<HStack gap="5px">
|
||||||
<SettingRow>
|
<Button onClick={reset} danger>
|
||||||
<SettingRowTitle>{t('settings.general.reset.title')}</SettingRowTitle>
|
{t('settings.general.reset.button')}
|
||||||
<HStack gap="5px">
|
</Button>
|
||||||
<Button onClick={reset} danger>
|
</HStack>
|
||||||
{t('settings.general.reset.button')}
|
</SettingRow>
|
||||||
</Button>
|
</SettingGroup>
|
||||||
</HStack>
|
<SettingGroup>
|
||||||
</SettingRow>
|
<WebDavSettings />
|
||||||
<SettingDivider />
|
</SettingGroup>
|
||||||
</SettingContainer>
|
<SettingGroup>
|
||||||
}
|
<SettingTitle>{t('settings.data.title')}</SettingTitle>
|
||||||
/>
|
<SettingDivider />
|
||||||
<Route path="webdav" element={<WebDavSettings />} />
|
<SettingRow>
|
||||||
</Routes>
|
<SettingRowTitle>{t('settings.data.app_data')}</SettingRowTitle>
|
||||||
|
<HStack alignItems="center" gap="5px">
|
||||||
|
<Typography.Text style={{ color: 'var(--color-text-3)' }}>{appInfo?.appDataPath}</Typography.Text>
|
||||||
|
<StyledIcon onClick={() => handleOpenPath(appInfo?.appDataPath)} />
|
||||||
|
</HStack>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>{t('settings.data.app_logs')}</SettingRowTitle>
|
||||||
|
<HStack alignItems="center" gap="5px">
|
||||||
|
<Typography.Text style={{ color: 'var(--color-text-3)' }}>{appInfo?.logsPath}</Typography.Text>
|
||||||
|
<StyledIcon onClick={() => handleOpenPath(appInfo?.logsPath)} />
|
||||||
|
</HStack>
|
||||||
|
</SettingRow>
|
||||||
|
</SettingGroup>
|
||||||
|
</SettingContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StyledIcon = styled(FileSearchOutlined)`
|
||||||
|
color: var(--color-text-2);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default DataSettings
|
export default DataSettings
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import {
|
|||||||
setWebdavPath as _setWebdavPath,
|
setWebdavPath as _setWebdavPath,
|
||||||
setWebdavUser as _setWebdavUser
|
setWebdavUser as _setWebdavUser
|
||||||
} from '@renderer/store/settings'
|
} from '@renderer/store/settings'
|
||||||
import { Breadcrumb, Button, Input } from 'antd'
|
import { Button, Input } from 'antd'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
import { SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
|
|
||||||
const WebDavSettings: FC = () => {
|
const WebDavSettings: FC = () => {
|
||||||
const {
|
const {
|
||||||
@ -58,19 +58,8 @@ const WebDavSettings: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingContainer>
|
<>
|
||||||
<Breadcrumb
|
<SettingTitle>{t('settings.data.webdav.title')}</SettingTitle>
|
||||||
items={[
|
|
||||||
{
|
|
||||||
title: t('settings.data'),
|
|
||||||
href: '#/settings/data'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('settings.data.webdav.title')
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<SettingTitle style={{ marginTop: 20 }}>{t('settings.data.webdav.title')}</SettingTitle>
|
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitle>{t('settings.data.webdav.host')}</SettingRowTitle>
|
<SettingRowTitle>{t('settings.data.webdav.host')}</SettingRowTitle>
|
||||||
@ -129,8 +118,7 @@ const WebDavSettings: FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider />
|
</>
|
||||||
</SettingContainer>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -96,8 +96,13 @@ const ShortcutSettings: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Table = styled(AntTable)`
|
const Table = styled(AntTable)`
|
||||||
|
.ant-table {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-table-cell {
|
.ant-table-cell {
|
||||||
padding: 14px 0 !important;
|
padding: 14px 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -21,8 +21,9 @@ export const SettingTitle = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-weight: 900;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const SettingSubtitle = styled.div`
|
export const SettingSubtitle = styled.div`
|
||||||
@ -42,6 +43,7 @@ export const SettingRow = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
min-height: 32px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const SettingRowTitle = styled.div`
|
export const SettingRowTitle = styled.div`
|
||||||
@ -67,3 +69,10 @@ export const SettingHelpLink = styled(Link)`
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const SettingGroup = styled.div`
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
padding: 16px;
|
||||||
|
`
|
||||||
|
|||||||
@ -149,3 +149,12 @@ export type WebDavConfig = {
|
|||||||
webdavPass: string
|
webdavPass: string
|
||||||
webdavPath: string
|
webdavPath: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AppInfo = {
|
||||||
|
version: string
|
||||||
|
isPackaged: boolean
|
||||||
|
appPath: string
|
||||||
|
appDataPath: string
|
||||||
|
filesPath: string
|
||||||
|
logsPath: string
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user