Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/main/presenter/filePresenter/FilePresenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from 'path'

import { BaseFileAdapter } from './BaseFileAdapter'
import { FileAdapterConstructor } from './FileAdapterConstructor'
import { FileOperation } from '../../../shared/presenter'
import { FileOperation, IConfigPresenter } from '../../../shared/presenter'
import { detectMimeType, getMimeTypeAdapterMap } from './mime'
import { IFilePresenter } from '../../../shared/presenter'
import { MessageFile } from '@shared/chat'
Expand All @@ -21,13 +21,18 @@ import {

export class FilePresenter implements IFilePresenter {
private userDataPath: string
private maxFileSize: number = 1024 * 1024 * 30 // 30 MB
private configPresenter: IConfigPresenter
private tempDir: string
private fileValidationService: IFileValidationService

constructor(fileValidationService?: IFileValidationService) {
get maxFileSize(): number {
return this.configPresenter.getSetting<number>('maxFileSize') ?? 1024 * 1024 * 30 //30MB
}

constructor(configPresenter: IConfigPresenter, fileValidationService?: IFileValidationService) {
this.userDataPath = app.getPath('userData')
this.tempDir = path.join(this.userDataPath, 'temp')
this.configPresenter = configPresenter
this.fileValidationService = fileValidationService || new FileValidationService()
// Ensure temp directory exists
try {
Expand Down
2 changes: 1 addition & 1 deletion src/main/presenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class Presenter implements IPresenter {
this.mcpPresenter = new McpPresenter(this.configPresenter)
this.upgradePresenter = new UpgradePresenter(this.configPresenter)
this.shortcutPresenter = new ShortcutPresenter(this.configPresenter)
this.filePresenter = new FilePresenter()
this.filePresenter = new FilePresenter(this.configPresenter)
this.syncPresenter = new SyncPresenter(this.configPresenter, this.sqlitePresenter)
this.deeplinkPresenter = new DeeplinkPresenter()
this.notificationPresenter = new NotificationPresenter()
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/settings/components/CommonSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<div class="w-full h-full flex flex-col gap-3 p-4">
<SearchEngineSettingsSection />
<WebContentLimitSetting />
<UploadFileSettingsSection />
<SearchAssistantModelSection />
<ProxySettingsSection />
<SettingToggleRow
Expand Down Expand Up @@ -50,6 +51,7 @@ import SearchAssistantModelSection from './common/SearchAssistantModelSection.vu
import ProxySettingsSection from './common/ProxySettingsSection.vue'
import LoggingSettingsSection from './common/LoggingSettingsSection.vue'
import SettingToggleRow from './common/SettingToggleRow.vue'
import UploadFileSettingsSection from './common/UploadFileSettingsSection.vue'

const { t } = useI18n()
const uiSettingsStore = useUiSettingsStore()
Expand Down
46 changes: 41 additions & 5 deletions src/renderer/settings/components/KnowledgeFileItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,21 @@
icon="lucide:circle-check-big"
class="text-base text-green-500"
/>
<Icon
<div
v-else-if="file.status === 'processing'"
icon="lucide:loader"
class="text-base text-blue-500 animate-spin"
/>
class="relative group w-6 h-6 flex items-center justify-center"
>
<Icon icon="lucide:loader" class="text-base text-blue-500 animate-spin" />
<!-- Tooltip -->
<div
class="absolute bottom-full mb-1 w-max px-2 py-0.5 rounded-md bg-card text-muted-foreground text-xs opacity-0 group-hover:opacity-100 transition-opacity shadow-md pointer-events-none whitespace-nowrap"
>
{{ Math.floor(progressPercent) }}% {{ progress.completed + progress.error }}/{{
progress.total
}}
</div>
</div>

<Icon
v-else-if="file.status === 'error'"
icon="lucide:circle-alert"
Expand Down Expand Up @@ -108,7 +118,7 @@ import { Icon } from '@iconify/vue'
import { useI18n } from 'vue-i18n'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
import { computed } from 'vue'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { KnowledgeFileMessage } from '@shared/presenter'
import {
AlertDialog,
Expand All @@ -123,6 +133,7 @@ import {
} from '@shadcn/components/ui/alert-dialog'
import { Button } from '@shadcn/components/ui/button'
import dayjs from 'dayjs'
import { RAG_EVENTS } from '@/events'

dayjs.extend(utc)
dayjs.extend(timezone)
Expand Down Expand Up @@ -179,4 +190,29 @@ const getStatusTitle = (status: string): string => {
return t(`settings.knowledgeBase.unknown`)
}
}

const progress = ref({ completed: 0, error: 0, total: 0 })
const progressPercent = computed(() => {
if (!progress.value.total) return 0
return ((progress.value.completed + progress.value.error) / progress.value.total) * 100
})

onMounted(async () => {
window.electron.ipcRenderer.on(
RAG_EVENTS.FILE_PROGRESS,
(_, data: { fileId: string; completed: number; error: number; total: number }) => {
if (props.file.id === data.fileId) {
progress.value = {
completed: data.completed,
error: data.error,
total: data.total
}
}
}
)
})

onBeforeUnmount(() => {
window.electron.ipcRenderer.removeAllListeners(RAG_EVENTS.FILE_PROGRESS)
})
</script>
145 changes: 145 additions & 0 deletions src/renderer/settings/components/common/UploadFileSettingsSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<template>
<div class="flex flex-row items-center gap-2 h-10">
<span class="flex flex-row items-center gap-2 grow w-full">
<Icon icon="lucide:file" class="w-4 h-4 text-muted-foreground" />
<span class="text-sm font-medium">{{ t('settings.common.fileMaxSize') }}</span>
<div class="text-xs text-muted-foreground ml-1">
{{ t('settings.common.fileMaxSizeHint') }}
</div>
</span>

<div class="shrink-0 flex items-center gap-1">
<!-- 减小按钮 -->
<Button
variant="outline"
size="icon"
class="h-8 w-8"
@click="decreaseFileMaxSize"
:disabled="fileMaxSize <= minSize"
>
<Icon icon="lucide:minus" class="h-3 w-3" />
</Button>

<!-- 当前值 / 输入框 -->
<div class="relative">
<div
v-if="!isEditing"
@click="startEditing"
class="min-w-16 h-8 flex items-center justify-center text-sm font-semibold cursor-pointer hover:bg-accent rounded px-2"
>
{{ fileMaxSize }}
</div>
<Input
v-else
ref="inputRef"
type="number"
:min="minSize"
:max="maxSize"
:model-value="fileMaxSize"
@update:model-value="handleChange"
@blur="stopEditing"
@keydown.enter="stopEditing"
@keydown.escape="stopEditing"
class="min-w-16 h-8 text-center text-sm font-semibold rounded px-2"
:class="{ 'bg-accent': isEditing }"
/>
</div>

<!-- 增大按钮 -->
<Button
variant="outline"
size="icon"
class="h-8 w-8"
@click="increaseFileMaxSize"
:disabled="fileMaxSize >= maxSize"
>
<Icon icon="lucide:plus" class="h-3 w-3" />
</Button>

<!-- 单位 -->
<span class="text-xs text-muted-foreground ml-1">{{ 'MB' }}</span>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, nextTick, onMounted, watch } from 'vue'
import { Icon } from '@iconify/vue'
import { Button } from '@shadcn/components/ui/button'
import { Input } from '@shadcn/components/ui/input'
import { useI18n } from 'vue-i18n'
import { usePresenter } from '@/composables/usePresenter'

const { t } = useI18n()
const configPresenter = usePresenter('configPresenter')

const minSize = 1
const maxSize = 1024 // 1024MB

const fileMaxSize = ref(30) // 默认值
const isEditing = ref(false)
const inputRef = ref<{ dom: HTMLInputElement }>()

const handleChange = async (value: string | number) => {
const numValue = typeof value === 'string' ? parseInt(value, 10) : value
if (!isNaN(numValue) && numValue >= minSize && numValue <= maxSize) {
try {
await configPresenter.setSetting('maxFileSize', numValue * 1024 * 1024)
fileMaxSize.value = numValue
} catch (error) {
console.error('Failed to set max file size:', error)
}
}
}

const increaseFileMaxSize = () => {
const newValue = Math.min(fileMaxSize.value + 50, maxSize)
handleChange(newValue)
}

const decreaseFileMaxSize = () => {
const newValue = Math.max(fileMaxSize.value - 50, minSize)
handleChange(newValue)
}

const startEditing = () => {
isEditing.value = true
}

const stopEditing = () => {
isEditing.value = false
}

watch(
() => isEditing.value,
async (newVal) => {
if (newVal) {
await nextTick()
inputRef.value?.dom?.focus?.()
}
}
)

onMounted(async () => {
try {
const saved = await configPresenter.getSetting<number>('maxFileSize')
if (saved !== undefined && saved !== null) {
fileMaxSize.value = saved / 1024 / 1024
}
} catch (error) {
console.error('Failed to load max file size:', error)
}
})
</script>

<style scoped>
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}

input[type='number'] {
-moz-appearance: textfield;
}
</style>
3 changes: 2 additions & 1 deletion src/renderer/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ export const DIALOG_EVENTS = {

// 知识库事件
export const RAG_EVENTS = {
FILE_UPDATED: 'rag:file-updated' // 文件状态更新
FILE_UPDATED: 'rag:file-updated', // 文件状态更新
FILE_PROGRESS: 'rag:file-progress' // 文件进度更新
}
// 系统相关事件
export const SYSTEM_EVENTS = {
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/en-US/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
},
"notifications": "System Notifications",
"notificationsDesc": "When DeepChat is not in the foreground, if a session is generated, a system notification will be sent",
"contentProtection": "Screen capture protection"
"contentProtection": "Screen capture protection",
"fileMaxSize": "File Maximum Size",
"fileMaxSizeHint": "Limits the maximum size of a single uploaded file"
},
"data": {
"title": "Data Settings",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/fa-IR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
},
"notifications": "آگاه‌ساز سامانه",
"notificationsDesc": "هنگامی که دیپ‌چت در پیش‌زمینه نیست، اگر نشستی تولید شود، یک آگاه‌ساز سامانه فرستاده خواهد شد",
"traceDebugEnabled": "پیگیری تماس"
"traceDebugEnabled": "پیگیری تماس",
"fileMaxSize": "حداکثر اندازه فایل",
"fileMaxSizeHint": "حداکثر اندازه یک فایل قابل آپلود را محدود می‌کند"
},
"data": {
"title": "تنظیمات داده",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/fr-FR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
},
"notifications": "Notifications système",
"notificationsDesc": "Quand DeepChat est en arrière‑plan, envoyer une notification à la fin d’une réponse",
"traceDebugEnabled": "Suivi des appels"
"traceDebugEnabled": "Suivi des appels",
"fileMaxSize": "Taille maximale du fichier",
"fileMaxSizeHint": "Limite la taille maximale d'un fichier à télécharger"
},
"data": {
"title": "Paramètres des données",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/ja-JP/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
},
"notifications": "システム通知",
"notificationsDesc": "DeepChatがバックグラウンドのとき、応答が完了すると通知を送信します",
"traceDebugEnabled": "追跡呼び出し"
"traceDebugEnabled": "追跡呼び出し",
"fileMaxSize": "ファイルの最大サイズ",
"fileMaxSizeHint": "アップロードできるファイルの最大サイズを制限します"
},
"data": {
"title": "データ設定",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/ko-KR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
},
"notifications": "시스템 알림",
"notificationsDesc": "DeepChat이 전경에 있지 않으면 세션이 생성되면 시스템 알림이 전송됩니다.",
"traceDebugEnabled": "추적 호출"
"traceDebugEnabled": "추적 호출",
"fileMaxSize": "파일 최대 크기",
"fileMaxSizeHint": "단일 파일 업로드의 최대 크기를 제한합니다"
},
"data": {
"title": "데이터 설정",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/pt-BR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
"notifications": "Notificações do Sistema",
"notificationsDesc": "Quando o DeepChat não estiver em primeiro plano, se uma sessão for gerada, uma notificação do sistema será enviada",
"contentProtection": "Proteção contra captura de tela",
"traceDebugEnabled": "Rastreamento de Chamadas"
"traceDebugEnabled": "Rastreamento de Chamadas",
"fileMaxSize": "Tamanho máximo do arquivo",
"fileMaxSizeHint": "Limita o tamanho máximo de um arquivo enviado"
},
"data": {
"title": "Configurações de Dados",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/ru-RU/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
"themeSystem": "Следовать системе",
"notifications": "Системное уведомление",
"notificationsDesc": "Когда DeepChat не будет на переднем плане, если сгенерируется сеанс, будет отправлено системное уведомление",
"traceDebugEnabled": "Функция отладки Trace"
"traceDebugEnabled": "Функция отладки Trace",
"fileMaxSize": "Максимальный размер файла",
"fileMaxSizeHint": "Ограничивает максимальный размер загружаемого файла"
},
"data": {
"title": "Настройки данных",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/zh-CN/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
"loggingRestartNotice": "切换此设置将导致程序重启,请确认是否继续?",
"openLogFolder": "打开日志文件夹",
"notifications": "系统通知",
"notificationsDesc": "当 DeepChat 不在前台时,如有会话生成完毕会发送系统通知"
"notificationsDesc": "当 DeepChat 不在前台时,如有会话生成完毕会发送系统通知",
"fileMaxSize": "文件最大大小",
"fileMaxSizeHint": "限制单个文件的最大上传大小"
},
"data": {
"title": "数据设置",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/zh-HK/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
},
"notifications": "系統通知",
"notificationsDesc": "當 DeepChat 不在前台時,如有會話生成完畢會發送系統通知",
"traceDebugEnabled": "Trace 除錯功能"
"traceDebugEnabled": "Trace 除錯功能",
"fileMaxSize": "檔案最大大小",
"fileMaxSizeHint": "限制單個檔案的最大上傳大小"
},
"data": {
"title": "數據設置",
Expand Down
4 changes: 3 additions & 1 deletion src/renderer/src/i18n/zh-TW/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@
},
"notifications": "系統通知",
"notificationsDesc": "當 DeepChat 不在前台時,如有會話生成完畢會發送系統通知",
"traceDebugEnabled": "Trace 除錯功能"
"traceDebugEnabled": "Trace 除錯功能",
"fileMaxSize": "檔案最大大小",
"fileMaxSizeHint": "限制單個檔案的最大上傳大小"
},
"data": {
"title": "資料設定",
Expand Down
Loading