diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 50fcfa3a..b3e64634 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -1,4 +1,4 @@ -import { FileMetadata } from '@types' +import { FileType } from '@types' import { BrowserWindow, ipcMain, OpenDialogOptions, session, shell } from 'electron' import Logger from 'electron-log' import fs from 'fs' @@ -55,7 +55,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { }) ipcMain.handle('file:select', async (_, options?: OpenDialogOptions) => await fileManager.selectFile(options)) - ipcMain.handle('file:upload', async (_, file: FileMetadata) => await fileManager.uploadFile(file)) + ipcMain.handle('file:upload', async (_, file: FileType) => await fileManager.uploadFile(file)) ipcMain.handle('file:delete', async (_, fileId: string) => { await fileManager.deleteFile(fileId) return { success: true } diff --git a/src/main/services/File.ts b/src/main/services/File.ts index 88fc8c5f..8cb3b090 100644 --- a/src/main/services/File.ts +++ b/src/main/services/File.ts @@ -1,5 +1,5 @@ import { getFileType } from '@main/utils/file' -import { FileMetadata } from '@types' +import { FileType } from '@types' import * as crypto from 'crypto' import { app, dialog, OpenDialogOptions } from 'electron' import * as fs from 'fs' @@ -30,7 +30,7 @@ class File { }) } - async findDuplicateFile(filePath: string): Promise { + async findDuplicateFile(filePath: string): Promise { const stats = fs.statSync(filePath) const fileSize = stats.size @@ -66,7 +66,7 @@ class File { return null } - async selectFile(options?: OpenDialogOptions): Promise { + async selectFile(options?: OpenDialogOptions): Promise { const defaultOptions: OpenDialogOptions = { properties: ['openFile'] } @@ -100,7 +100,7 @@ class File { return Promise.all(fileMetadataPromises) } - async uploadFile(file: FileMetadata): Promise { + async uploadFile(file: FileType): Promise { const duplicateFile = await this.findDuplicateFile(file.path) if (duplicateFile) { @@ -116,7 +116,7 @@ class File { const stats = await fs.promises.stat(destPath) const fileType = getFileType(ext) - const fileMetadata: FileMetadata = { + const fileMetadata: FileType = { id: uuid, origin_name, name: uuid + ext, diff --git a/src/main/utils/file.ts b/src/main/utils/file.ts index 580d138f..dcd94bf1 100644 --- a/src/main/utils/file.ts +++ b/src/main/utils/file.ts @@ -3,7 +3,7 @@ import logger from 'electron-log' import { writeFile } from 'fs' import { readFile } from 'fs/promises' -import { FileType } from '../../renderer/src/types' +import { FileTypes } from '../../renderer/src/types' export async function saveFile( _: Electron.IpcMainInvokeEvent, @@ -56,16 +56,16 @@ export async function openFile( } } -export function getFileType(ext: string): FileType { +export function getFileType(ext: string): FileTypes { const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] const videoExts = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv'] const audioExts = ['.mp3', '.wav', '.ogg', '.flac', '.aac'] const documentExts = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt'] ext = ext.toLowerCase() - if (imageExts.includes(ext)) return FileType.IMAGE - if (videoExts.includes(ext)) return FileType.VIDEO - if (audioExts.includes(ext)) return FileType.AUDIO - if (documentExts.includes(ext)) return FileType.DOCUMENT - return FileType.OTHER + if (imageExts.includes(ext)) return FileTypes.IMAGE + if (videoExts.includes(ext)) return FileTypes.VIDEO + if (audioExts.includes(ext)) return FileTypes.AUDIO + if (documentExts.includes(ext)) return FileTypes.DOCUMENT + return FileTypes.OTHER } diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index b1bec3ec..794aae04 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,5 +1,5 @@ import { ElectronAPI } from '@electron-toolkit/preload' -import { FileMetadata } from '@renderer/types' +import { FileType } from '@renderer/types' import type { OpenDialogOptions } from 'electron' declare global { @@ -22,8 +22,8 @@ declare global { compress: (text: string) => Promise decompress: (text: Buffer) => Promise file: { - select: (options?: OpenDialogOptions) => Promise - upload: (file: FileMetadata) => Promise + select: (options?: OpenDialogOptions) => Promise + upload: (file: FileType) => Promise delete: (fileId: string) => Promise<{ success: boolean }> } image: { diff --git a/src/renderer/src/databases/index.ts b/src/renderer/src/databases/index.ts index 73425630..11e01d56 100644 --- a/src/renderer/src/databases/index.ts +++ b/src/renderer/src/databases/index.ts @@ -1,9 +1,9 @@ -import { FileMetadata } from '@renderer/types' +import { FileType } from '@renderer/types' import { Dexie, type EntityTable } from 'dexie' // Database declaration (move this to its own module also) export const db = new Dexie('CherryStudio') as Dexie & { - files: EntityTable + files: EntityTable } db.version(1).stores({ diff --git a/src/renderer/src/hooks/useAppInit.ts b/src/renderer/src/hooks/useAppInit.ts index c9787bd3..40519826 100644 --- a/src/renderer/src/hooks/useAppInit.ts +++ b/src/renderer/src/hooks/useAppInit.ts @@ -11,8 +11,7 @@ import { useSettings } from './useSettings' export function useAppInit() { const dispatch = useAppDispatch() - const { proxyUrl } = useSettings() - const { language } = useSettings() + const { proxyUrl, language } = useSettings() const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel() useEffect(() => { diff --git a/src/renderer/src/pages/files/FilesPage.tsx b/src/renderer/src/pages/files/FilesPage.tsx index 8ecabbb8..8a581bfc 100644 --- a/src/renderer/src/pages/files/FilesPage.tsx +++ b/src/renderer/src/pages/files/FilesPage.tsx @@ -1,9 +1,8 @@ import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar' import { VStack } from '@renderer/components/Layout' import db from '@renderer/databases' -import FileManager from '@renderer/services/file' -import { FileMetadata } from '@renderer/types' -import { Button, Image, Table } from 'antd' +import { FileType } from '@renderer/types' +import { Image, Table } from 'antd' import dayjs from 'dayjs' import { useLiveQuery } from 'dexie-react-hooks' import { FC } from 'react' @@ -12,9 +11,10 @@ import styled from 'styled-components' const FilesPage: FC = () => { const { t } = useTranslation() - const files = useLiveQuery(() => db.files.toArray()) + const files = useLiveQuery(() => db.files.toArray()) const dataSource = files?.map((file) => ({ + key: file.id, file: , name: {file.name}, size: `${(file.size / 1024 / 1024).toFixed(2)} MB`, @@ -25,7 +25,8 @@ const FilesPage: FC = () => { { title: t('files.file'), dataIndex: 'file', - key: 'file' + key: 'file', + width: '300px' }, { title: t('files.name'), @@ -52,13 +53,6 @@ const FilesPage: FC = () => { {t('files.title')} - diff --git a/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx b/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx index 88be5a94..9fe7dff3 100644 --- a/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx @@ -1,14 +1,14 @@ import { PaperClipOutlined } from '@ant-design/icons' import { isVisionModel } from '@renderer/config/models' -import { FileMetadata, Model } from '@renderer/types' +import { FileType, Model } from '@renderer/types' import { Tooltip } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' interface Props { model: Model - files: FileMetadata[] - setFiles: (files: FileMetadata[]) => void + files: FileType[] + setFiles: (files: FileType[]) => void ToolbarButton: any } diff --git a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx index 3c52c05e..480a64bd 100644 --- a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx +++ b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx @@ -1,12 +1,12 @@ -import { FileMetadata } from '@renderer/types' +import { FileType } from '@renderer/types' import { Upload } from 'antd' import { isEmpty } from 'lodash' import { FC } from 'react' import styled from 'styled-components' interface Props { - files: FileMetadata[] - setFiles: (files: FileMetadata[]) => void + files: FileType[] + setFiles: (files: FileType[]) => void } const AttachmentPreview: FC = ({ files, setFiles }) => { diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 456db003..b43bc42f 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -1,11 +1,11 @@ import { ClearOutlined, ControlOutlined, + FormOutlined, FullscreenExitOutlined, FullscreenOutlined, HistoryOutlined, PauseCircleOutlined, - PlusCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons' import { useAssistant } from '@renderer/hooks/useAssistant' @@ -17,7 +17,7 @@ import FileManager from '@renderer/services/file' import { estimateInputTokenCount } from '@renderer/services/messages' import store, { useAppDispatch, useAppSelector } from '@renderer/store' import { setGenerating, setSearching } from '@renderer/store/runtime' -import { Assistant, FileMetadata, Message, Topic } from '@renderer/types' +import { Assistant, FileType, Message, Topic } from '@renderer/types' import { delay, uuid } from '@renderer/utils' import { Button, Popconfirm, Tooltip } from 'antd' import TextArea, { TextAreaRef } from 'antd/es/input/TextArea' @@ -49,7 +49,7 @@ const Inputbar: FC = ({ assistant, setActiveTopic }) => { const [contextCount, setContextCount] = useState(0) const generating = useAppSelector((state) => state.runtime.generating) const textareaRef = useRef(null) - const [files, setFiles] = useState([]) + const [files, setFiles] = useState([]) const { t } = useTranslation() const containerRef = useRef(null) const { showTopics, toggleShowTopics } = useShowTopics() @@ -229,7 +229,7 @@ const Inputbar: FC = ({ assistant, setActiveTopic }) => { - + diff --git a/src/renderer/src/services/file.ts b/src/renderer/src/services/file.ts index 25b4e6f1..495236bd 100644 --- a/src/renderer/src/services/file.ts +++ b/src/renderer/src/services/file.ts @@ -1,13 +1,13 @@ import db from '@renderer/databases' -import { FileMetadata } from '@renderer/types' +import { FileType } from '@renderer/types' class FileManager { - static async selectFiles(options?: Electron.OpenDialogOptions): Promise { + static async selectFiles(options?: Electron.OpenDialogOptions): Promise { const files = await window.api.file.select(options) return files } - static async uploadFile(file: FileMetadata): Promise { + static async uploadFile(file: FileType): Promise { const uploadFile = await window.api.file.upload(file) const fileRecord = await db.files.get(uploadFile.id) @@ -21,11 +21,11 @@ class FileManager { return uploadFile } - static async uploadFiles(files: FileMetadata[]): Promise { + static async uploadFiles(files: FileType[]): Promise { return Promise.all(files.map((file) => this.uploadFile(file))) } - static async getFile(id: string): Promise { + static async getFile(id: string): Promise { return db.files.get(id) } @@ -49,7 +49,7 @@ class FileManager { await Promise.all(ids.map((id) => this.deleteFile(id))) } - static async allFiles(): Promise { + static async allFiles(): Promise { return db.files.toArray() } } diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 29f8bec1..1132af48 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -27,7 +27,7 @@ export type Message = { createdAt: string status: 'sending' | 'pending' | 'success' | 'paused' | 'error' modelId?: string - files?: FileMetadata[] + files?: FileType[] images?: string[] usage?: OpenAI.Completions.CompletionUsage type?: 'text' | '@' | 'clear' @@ -87,7 +87,7 @@ export type MinAppType = { url: string } -export interface FileMetadata { +export interface FileType { id: string name: string origin_name: string @@ -99,7 +99,7 @@ export interface FileMetadata { count: number } -export enum FileType { +export enum FileTypes { IMAGE = 'image', VIDEO = 'video', AUDIO = 'audio',