Skip to content

Commit 3118f3e

Browse files
committed
feat: 增强复制和文件上传功能
- 在CopyButton.tsx中添加自动保存历史记录功能 - 复制成功时自动保存当前配置到历史记录 - 在FileUpload.tsx中添加PDF文件处理支持 - 支持PDF文件拖拽上传并自动转换为Markdown - 添加处理进度显示和状态提示 - 扩展文件类型支持,提升用户体验
1 parent eb7aeed commit 3118f3e

File tree

2 files changed

+158
-24
lines changed

2 files changed

+158
-24
lines changed

src/pages/CopyButton.tsx

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import React, {useMemo, useState} from 'react';
1+
import React, {useMemo, useState, useEffect} from 'react';
22
import Button from '@mui/material/Button';
33
import Snackbar from '@mui/material/Snackbar';
44
import Alert from '@mui/material/Alert';
55
import {FileInfo} from "../types/types.ts";
66
import {keyframes} from '@mui/system';
77
import {calculateTokenCount, formatTokenCount, generatePrompt} from "../utils/promptBuilder";
8+
import {HistoryRecord} from "../types/history";
9+
import {historyDB} from "../utils/indexedDB";
810

911
interface CopyButtonProps {
1012
roleContent: string;
@@ -41,6 +43,20 @@ const CopyButton: React.FC<CopyButtonProps> = ({
4143
text: '',
4244
severity: 'success',
4345
});
46+
const [isDBInitialized, setIsDBInitialized] = useState(false);
47+
48+
// 初始化IndexedDB
49+
useEffect(() => {
50+
const initDB = async () => {
51+
try {
52+
await historyDB.init();
53+
setIsDBInitialized(true);
54+
} catch (error) {
55+
console.error('Failed to initialize IndexedDB:', error);
56+
}
57+
};
58+
initDB();
59+
}, []);
4460

4561
const tokenCount = useMemo(() =>
4662
calculateTokenCount(roleContent, ruleContent, taskContent, outputContent, files),
@@ -51,11 +67,41 @@ const CopyButton: React.FC<CopyButtonProps> = ({
5167
formatTokenCount(tokenCount),
5268
[tokenCount]);
5369

70+
// 自动保存历史记录
71+
const saveToHistory = async () => {
72+
if (!isDBInitialized) return;
73+
74+
try {
75+
const record: HistoryRecord = {
76+
id: `copy_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
77+
timestamp: Date.now(),
78+
title: `复制记录 ${new Date().toLocaleString('zh-CN')}`,
79+
description: '通过复制按钮自动保存的历史记录',
80+
roleContent,
81+
ruleContent,
82+
taskContent,
83+
outputContent,
84+
files,
85+
tags: ['自动保存', '复制'],
86+
version: 1
87+
};
88+
89+
await historyDB.saveRecord(record);
90+
console.log('History record saved automatically on copy');
91+
} catch (error) {
92+
console.error('Failed to save history record on copy:', error);
93+
}
94+
};
95+
5496
const handleCopy = async () => {
5597
try {
56-
await navigator.clipboard.writeText(
57-
generatePrompt(roleContent, ruleContent, taskContent, outputContent, files)
58-
);
98+
const promptText = generatePrompt(roleContent, ruleContent, taskContent, outputContent, files);
99+
await navigator.clipboard.writeText(promptText);
100+
101+
// 复制成功后自动保存历史记录
102+
await saveToHistory();
103+
104+
setMessage({text: '复制成功,已自动保存到历史记录', severity: 'success'});
59105
} catch {
60106
setMessage({text: '复制失败', severity: 'error'});
61107
}

src/pages/FileUpload.tsx

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// FileUpload.tsx
22
import React, {useState, useRef} from 'react';
33
import {FileInfo} from "../types/types.ts";
4+
import { convertPDFToMarkdown, convertPDFResultToFileInfo, isPDFFile } from '../utils/pdfProcessor';
5+
import { Alert, LinearProgress, Typography, Box } from '@mui/material';
46

57
interface FileUploadProps {
68
onFilesUploaded: (files: FileInfo[]) => void;
@@ -43,9 +45,18 @@ const getFilesFromDirectory = async (directory: FileSystemDirectoryEntry): Promi
4345

4446
const FileUpload: React.FC<FileUploadProps> = ({onFilesUploaded}) => {
4547
const [isDragging, setIsDragging] = useState(false);
48+
const [isProcessingPDF, setIsProcessingPDF] = useState(false);
49+
const [processingStatus, setProcessingStatus] = useState<string>('');
50+
const [processingProgress, setProcessingProgress] = useState(0);
4651
const fileInputRef = useRef<HTMLInputElement>(null);
4752

4853
const readFileContent = async (file: File): Promise<FileInfo> => {
54+
// 检查是否为PDF文件
55+
if (isPDFFile(file)) {
56+
return await processPDFFile(file);
57+
}
58+
59+
// 处理普通文本文件
4960
return new Promise((resolve) => {
5061
const reader = new FileReader();
5162
reader.onload = (e) => {
@@ -61,6 +72,57 @@ const FileUpload: React.FC<FileUploadProps> = ({onFilesUploaded}) => {
6172
});
6273
};
6374

75+
const processPDFFile = async (file: File): Promise<FileInfo> => {
76+
setIsProcessingPDF(true);
77+
setProcessingStatus(`正在处理PDF文件: ${file.name}`);
78+
setProcessingProgress(0);
79+
80+
try {
81+
// 模拟进度更新
82+
const progressInterval = setInterval(() => {
83+
setProcessingProgress(prev => Math.min(prev + 10, 90));
84+
}, 200);
85+
86+
const result = await convertPDFToMarkdown(file, {
87+
maxPages: 20,
88+
preserveFormatting: true,
89+
includeImages: false
90+
});
91+
92+
clearInterval(progressInterval);
93+
setProcessingProgress(100);
94+
95+
if (result.success) {
96+
setProcessingStatus(`PDF处理完成: ${result.metadata.processedPages}/${result.metadata.pages} 页`);
97+
setTimeout(() => {
98+
setIsProcessingPDF(false);
99+
setProcessingStatus('');
100+
setProcessingProgress(0);
101+
}, 1500);
102+
103+
return convertPDFResultToFileInfo(file, result);
104+
} else {
105+
throw new Error(result.error || 'PDF处理失败');
106+
}
107+
} catch (error) {
108+
setProcessingStatus(`PDF处理失败: ${error}`);
109+
setTimeout(() => {
110+
setIsProcessingPDF(false);
111+
setProcessingStatus('');
112+
setProcessingProgress(0);
113+
}, 3000);
114+
115+
// 返回错误信息作为文件内容
116+
return {
117+
name: file.name.replace('.pdf', '_error.txt'),
118+
path: file.name,
119+
size: 0,
120+
extension: 'txt',
121+
content: `PDF处理失败: ${error}\n\n原文件: ${file.name}\n大小: ${file.size} bytes`
122+
};
123+
}
124+
};
125+
64126
const processEntries = async (items: DataTransferItemList) => {
65127
const files: File[] = [];
66128

@@ -105,26 +167,52 @@ const FileUpload: React.FC<FileUploadProps> = ({onFilesUploaded}) => {
105167
};
106168

107169
return (
108-
<div
109-
className={`drop-zone ${isDragging ? 'dragging' : ''}`}
110-
onDragOver={(e) => {
111-
e.preventDefault();
112-
setIsDragging(true);
113-
}}
114-
onDragLeave={() => setIsDragging(false)}
115-
onDrop={handleFileDrop}
116-
onClick={handleClick}
117-
style={{cursor: 'pointer'}}
118-
>
119-
<input
120-
type="file"
121-
multiple
122-
// webkitdirectory=""
123-
onChange={handleFileSelect}
124-
className="file-input"
125-
ref={fileInputRef}
126-
/>
127-
<p>拖放AI参考代码文件/文件夹到这里或点击此处选择(参考上下文越完整,AI推理结果越好)</p>
170+
<div>
171+
<div
172+
className={`drop-zone ${isDragging ? 'dragging' : ''}`}
173+
onDragOver={(e) => {
174+
e.preventDefault();
175+
setIsDragging(true);
176+
}}
177+
onDragLeave={() => setIsDragging(false)}
178+
onDrop={handleFileDrop}
179+
onClick={!isProcessingPDF ? handleClick : undefined}
180+
style={{cursor: isProcessingPDF ? 'default' : 'pointer'}}
181+
>
182+
<input
183+
type="file"
184+
multiple
185+
accept=".txt,.js,.ts,.jsx,.tsx,.py,.java,.cpp,.c,.h,.cs,.php,.rb,.go,.rs,.swift,.kt,.scala,.clj,.html,.css,.scss,.sass,.less,.xml,.json,.yaml,.yml,.md,.sql,.sh,.bat,.ps1,.dockerfile,.gitignore,.env,.config,.ini,.toml,.lock,.pdf"
186+
onChange={handleFileSelect}
187+
className="file-input"
188+
ref={fileInputRef}
189+
disabled={isProcessingPDF}
190+
/>
191+
<p>
192+
拖放AI参考代码文件/文件夹到这里或点击此处选择
193+
<br />
194+
<small style={{ color: '#666', fontSize: '0.9em' }}>
195+
支持代码文件和PDF文档(PDF将自动转换为Markdown)
196+
</small>
197+
</p>
198+
</div>
199+
200+
{/* PDF处理状态显示 */}
201+
{isProcessingPDF && (
202+
<Box sx={{ mt: 2, p: 2, border: '1px solid #ddd', borderRadius: 1 }}>
203+
<Typography variant="body2" color="primary" gutterBottom>
204+
{processingStatus}
205+
</Typography>
206+
<LinearProgress
207+
variant="determinate"
208+
value={processingProgress}
209+
sx={{ mb: 1 }}
210+
/>
211+
<Typography variant="caption" color="text.secondary">
212+
{processingProgress}% 完成
213+
</Typography>
214+
</Box>
215+
)}
128216
</div>
129217
);
130218
};

0 commit comments

Comments
 (0)