diff --git a/database/database.js b/database/database.js new file mode 100644 index 0000000..fb8e326 --- /dev/null +++ b/database/database.js @@ -0,0 +1,235 @@ +const sqlite3 = require('sqlite3') +const path = require('path') +const { open } = require('sqlite') +const fs = require('fs-extra') + +const dbPath = path.join(process.cwd(), 'database/files.db') + +// 确保数据库目录存在 +const dbDir = path.dirname(dbPath) +fs.ensureDirSync(dbDir) + +async function initDatabase() { + const db = await open({ + filename: dbPath, + driver: sqlite3.Database + }) + + // 设置数据库编码为 UTF-8 + await db.exec('PRAGMA encoding = "UTF-8"') + await db.exec('PRAGMA foreign_keys = ON') + + await db.exec(` + CREATE TABLE IF NOT EXISTS files ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + original_name TEXT NOT NULL, + file_name TEXT NOT NULL, + file_path TEXT NOT NULL, + file_size INTEGER NOT NULL, + mime_type TEXT NOT NULL, + md5 TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `) + + // 新增 OCR 结果表 + await db.exec(` + CREATE TABLE IF NOT EXISTS ocr_results ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file_id INTEGER NOT NULL, + ocr_data TEXT NOT NULL, + confidence REAL, + processing_time INTEGER, + recognized_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (file_id) REFERENCES files (id) ON DELETE CASCADE, + UNIQUE(file_id) + ) + `) + + await db.close() +} + +class FileService { + async getDb() { + const db = await open({ + filename: dbPath, + driver: sqlite3.Database + }) + + // 确保每次连接都使用 UTF-8 + await db.exec('PRAGMA encoding = "UTF-8"') + return db + } + + async createFile(fileData) { + const db = await this.getDb() + + // 确保文件名正确存储 + const result = await db.run( + `INSERT INTO files (original_name, file_name, file_path, file_size, mime_type, md5) + VALUES (?, ?, ?, ?, ?, ?)`, + [ + fileData.originalName, + fileData.fileName, + fileData.filePath, + fileData.fileSize, + fileData.mimeType, + fileData.md5 + ] + ) + + const file = await db.get( + 'SELECT * FROM files WHERE id = ?', + result.lastID + ) + + await db.close() + + return this.mapDatabaseToFileRecord(file) + } + + async getFilesPaginated(page, pageSize) { + const db = await this.getDb() + const offset = (page - 1) * pageSize + + const files = await db.all( + 'SELECT * FROM files ORDER BY created_at DESC LIMIT ? OFFSET ?', + [pageSize, offset] + ) + + const totalResult = await db.get('SELECT COUNT(*) as count FROM files') + const total = totalResult.count + + await db.close() + + return { + files: files.map(file => this.mapDatabaseToFileRecord(file)), + pagination: { + page, + pageSize, + total, + totalPages: Math.ceil(total / pageSize) + } + } + } + + async getFileById(id) { + const db = await this.getDb() + const file = await db.get('SELECT * FROM files WHERE id = ?', [id]) + await db.close() + return file ? this.mapDatabaseToFileRecord(file) : null + } + + async updateFileMD5(id, md5) { + const db = await this.getDb() + await db.run( + 'UPDATE files SET md5 = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', + [md5, id] + ) + await db.close() + } + + mapDatabaseToFileRecord(dbFile) { + // 确保从数据库读取时正确处理编码 + let originalName = dbFile.original_name + try { + // 尝试解码,如果已经是正确编码则不会影响 + originalName = decodeURIComponent(originalName) + } catch (error) { + console.warn('文件名解码失败,使用原值:', error) + } + + return { + id: dbFile.id, + originalName: originalName, + fileName: dbFile.file_name, + filePath: dbFile.file_path, + fileSize: dbFile.file_size, + mimeType: dbFile.mime_type, + md5: dbFile.md5, + createdAt: dbFile.created_at, + updatedAt: dbFile.updated_at + } + } + async saveOcrResult(fileId, ocrData) { + const db = await this.getDb() + + // 将 OCR 数据转为 JSON 字符串存储 + const ocrDataJson = JSON.stringify(ocrData) + + try { + // 尝试更新已存在的记录 + const result = await db.run( + `UPDATE ocr_results SET ocr_data = ?, confidence = ?, processing_time = ?, updated_at = CURRENT_TIMESTAMP + WHERE file_id = ?`, + [ocrDataJson, ocrData.confidence, ocrData.processingTime, fileId] + ) + + // 如果没有更新任何行,则插入新记录 + if (result.changes === 0) { + await db.run( + `INSERT INTO ocr_results (file_id, ocr_data, confidence, processing_time) + VALUES (?, ?, ?, ?)`, + [fileId, ocrDataJson, ocrData.confidence, ocrData.processingTime] + ) + } + + await db.close() + return { success: true } + } catch (error) { + await db.close() + throw error + } + } + + async getOcrResult(fileId) { + const db = await this.getDb() + + const result = await db.get( + 'SELECT * FROM ocr_results WHERE file_id = ?', + [fileId] + ) + + await db.close() + + if (result) { + return { + ...result, + ocr_data: JSON.parse(result.ocr_data) + } + } + + return null + } + + async updateOcrText(fileId, newTextBlocks) { + const db = await this.getDb() + + const existingResult = await this.getOcrResult(fileId) + if (!existingResult) { + throw new Error('没有找到OCR结果') + } + + // 更新文本块 + const updatedOcrData = { + ...existingResult.ocr_data, + textBlocks: newTextBlocks, + updatedAt: new Date().toISOString(), + manuallyCorrected: true + } + + const ocrDataJson = JSON.stringify(updatedOcrData) + + await db.run( + 'UPDATE ocr_results SET ocr_data = ?, updated_at = CURRENT_TIMESTAMP WHERE file_id = ?', + [ocrDataJson, fileId] + ) + + await db.close() + return { success: true } + } +} + +module.exports = { initDatabase, FileService } \ No newline at end of file diff --git a/package.json b/package.json index 850302d..716ef50 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,17 @@ "preview": "vite preview" }, "dependencies": { + "canvas": "^3.2.0", "cors": "^2.8.5", "crypto-ts": "^1.0.2", "express": "^5.1.0", "fs-extra": "^11.3.2", "multer": "^2.0.2", + "node-tesseract-ocr": "^2.2.1", + "sharp": "^0.34.5", "sqlite": "^5.1.1", "sqlite3": "^5.1.7", + "tesseract.js": "^6.0.1", "vue": "^3.5.22", "vue-router": "^4.6.3" }, diff --git a/server/server.js b/server/server.js index b0d2a35..08b500c 100644 --- a/server/server.js +++ b/server/server.js @@ -6,6 +6,11 @@ const fs = require('fs-extra') const { calculateFileMD5 } = require('./utils.js') const { initDatabase, FileService } = require('../database/database.js') +// 新增 OCR 相关依赖 +const Tesseract = require('tesseract.js') +const sharp = require('sharp') +const { createCanvas, loadImage } = require('canvas') + const app = express() const PORT = 3000 @@ -13,9 +18,11 @@ const PORT = 3000 initDatabase() const fileService = new FileService() -// 确保上传目录存在 +// 确保上传目录和临时目录存在 const uploadDir = path.join(process.cwd(), 'uploads') +const tempDir = path.join(process.cwd(), 'temp') fs.ensureDirSync(uploadDir) +fs.ensureDirSync(tempDir) // 配置 multer - 修复中文文件名问题 const storage = multer.diskStorage({ @@ -93,23 +100,30 @@ app.post('/api/upload', upload.single('file'), async (req, res) => { } }) -// 获取文件列表(分页) +// 修复获取文件列表接口 - 确保返回正确的数据结构 app.get('/api/files', async (req, res) => { try { const page = parseInt(req.query.page) || 1 - const pageSize = parseInt(req.query.pageSize) || 10 + const pageSize = parseInt(req.query.pageSize) || 100 const result = await fileService.getFilesPaginated(page, pageSize) - // 确保返回的数据使用 UTF-8 编码 - res.json(result) + // 返回统一的数据结构 + res.json({ + success: true, + data: result.files, // 直接返回文件数组 + pagination: result.pagination + }) } catch (error) { console.error('Get files error:', error) - res.status(500).json({ error: 'Failed to get files' }) + res.status(500).json({ + success: false, + error: 'Failed to get files: ' + error.message + }) } }) -// 其他接口保持不变... +// MD5 检查接口 app.post('/api/files/:id/check-md5', async (req, res) => { try { const fileId = parseInt(req.params.id) @@ -134,6 +148,7 @@ app.post('/api/files/:id/check-md5', async (req, res) => { } }) +// 更新 MD5 接口 app.put('/api/files/:id/update-md5', async (req, res) => { try { const fileId = parseInt(req.params.id) @@ -147,6 +162,294 @@ app.put('/api/files/:id/update-md5', async (req, res) => { } }) +// 新增 OCR 识别接口 +app.post('/api/ocr/recognize', async (req, res) => { + try { + const { fileId, page } = req.body + + if (!fileId) { + return res.status(400).json({ error: 'File ID is required' }) + } + + const file = await fileService.getFileById(parseInt(fileId)) + if (!file) { + return res.status(404).json({ error: 'File not found' }) + } + + console.log(`开始OCR识别: ${file.originalName}`) + + // 预处理图像 + const processedImagePath = await preprocessImage(file.filePath) + + // 使用 Tesseract 进行 OCR 识别 + const result = await performOCR(processedImagePath) + + // 清理临时文件 + await fs.remove(processedImagePath) + + res.json({ + success: true, + data: { + textBlocks: result.textBlocks, + totalPages: result.totalPages || 1, + processingTime: result.processingTime, + confidence: result.confidence + } + }) + + } catch (error) { + console.error('OCR recognition error:', error) + res.status(500).json({ error: 'OCR recognition failed: ' + error.message }) + } +}) + +// 添加 OCR 结果相关的 API 接口 + +// 保存 OCR 结果 +app.post('/api/ocr/save-result', async (req, res) => { + try { + const { fileId, ocrData } = req.body + + if (!fileId || !ocrData) { + return res.status(400).json({ error: '文件ID和OCR数据是必需的' }) + } + + await fileService.saveOcrResult(parseInt(fileId), ocrData) + + res.json({ success: true }) + } catch (error) { + console.error('保存OCR结果失败:', error) + res.status(500).json({ error: '保存OCR结果失败: ' + error.message }) + } +}) + +// 获取 OCR 结果 +app.get('/api/ocr/result/:fileId', async (req, res) => { + try { + const fileId = parseInt(req.params.fileId) + const result = await fileService.getOcrResult(fileId) + + if (result) { + res.json({ + success: true, + data: result.ocr_data + }) + } else { + res.json({ + success: false, + error: '未找到OCR结果' + }) + } + } catch (error) { + console.error('获取OCR结果失败:', error) + res.status(500).json({ error: '获取OCR结果失败: ' + error.message }) + } +}) + +// 更新 OCR 文本(人工纠错) +app.put('/api/ocr/update-text', async (req, res) => { + try { + const { fileId, textBlocks } = req.body + + if (!fileId || !textBlocks) { + return res.status(400).json({ error: '文件ID和文本数据是必需的' }) + } + + await fileService.updateOcrText(parseInt(fileId), textBlocks) + + res.json({ success: true }) + } catch (error) { + console.error('更新OCR文本失败:', error) + res.status(500).json({ error: '更新OCR文本失败: ' + error.message }) + } +}) + +// 图像预处理函数 +async function preprocessImage(imagePath) { + const tempOutputPath = path.join(tempDir, `preprocessed-${Date.now()}.png`) + + try { + // 使用 sharp 进行图像预处理 + await sharp(imagePath) + .grayscale() // 转为灰度图 + .normalize() // 标准化图像 + .linear(1.5, 0) // 增加对比度 + .sharpen() // 锐化 + .png() + .toFile(tempOutputPath) + + return tempOutputPath + } catch (error) { + console.error('Image preprocessing failed:', error) + // 如果预处理失败,返回原图 + return imagePath + } +} + +// OCR 识别函数 +async function performOCR(imagePath) { + return new Promise((resolve, reject) => { + const startTime = Date.now() + + Tesseract.recognize( + imagePath, + 'chi_sim+eng', // 中文简体 + 英文 + { + logger: m => console.log(m), + tessedit_pageseg_mode: Tesseract.PSM.AUTO, + tessedit_char_whitelist: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\u4e00-\u9fa5,。!?;:"'/'()【】《》…—·' + } + ).then(({ data: { text, confidence } }) => { + const processingTime = Date.now() - startTime + + // 解析文本块 + const textBlocks = parseOCRText(text) + + resolve({ + textBlocks, + confidence, + processingTime + }) + }).catch(error => { + reject(error) + }) + }) +} + +// 解析 OCR 文本结果 +function parseOCRText(text) { + const blocks = [] + const lines = text.split('\n').filter(line => line.trim()) + + for (const line of lines) { + const trimmedLine = line.trim() + if (!trimmedLine) continue + + // 检测参考文献 + if (isReference(trimmedLine)) { + blocks.push({ + type: 'reference', + content: trimmedLine + }) + } + // 检测引用 + else if (isCitation(trimmedLine)) { + blocks.push({ + type: 'citation', + content: trimmedLine.replace(/^\[\d+\]\s*/, ''), + number: extractCitationNumber(trimmedLine) + }) + } + // 检测图片标记 + else if (isImageMarker(trimmedLine)) { + blocks.push({ + type: 'image', + content: trimmedLine + }) + } + // 普通文本 + else { + blocks.push({ + type: 'text', + content: trimmedLine + }) + } + } + + return blocks +} + +// 辅助函数 +function isReference(text) { + const refPatterns = [ + /^参考文献/i, + /^references/i, + /^bibliography/i, + /^\[?\d+\]?\s*\.?\s*[A-Za-z].*\.\s*\d{4}/ + ] + return refPatterns.some(pattern => pattern.test(text)) +} + +function isCitation(text) { + return /^\[\d+\]/.test(text) +} + +function extractCitationNumber(text) { + const match = text.match(/^\[(\d+)\]/) + return match ? parseInt(match[1]) : null +} + +function isImageMarker(text) { + const imagePatterns = [ + /^图\s*\d+/i, + /^figure\s*\d+/i, + /^图片\d*/i + ] + return imagePatterns.some(pattern => pattern.test(text)) +} + +// 获取文件预览接口 +app.get('/api/files/:id/preview', async (req, res) => { + try { + const fileId = parseInt(req.params.id) + const file = await fileService.getFileById(fileId) + + if (!file) { + return res.status(404).json({ error: 'File not found' }) + } + + // 检查文件是否存在 + if (!fs.existsSync(file.filePath)) { + return res.status(404).json({ error: 'File not found on disk' }) + } + + // 设置正确的 Content-Type + res.setHeader('Content-Type', file.mimeType) + + // 直接发送文件 + res.sendFile(path.resolve(file.filePath)) + + } catch (error) { + console.error('File preview error:', error) + res.status(500).json({ error: 'Failed to get file preview' }) + } +}) + +// 获取文件缩略图接口 +app.get('/api/files/:id/thumbnail', async (req, res) => { + try { + const fileId = parseInt(req.params.id) + const file = await fileService.getFileById(fileId) + + if (!file) { + return res.status(404).json({ error: 'File not found' }) + } + + // 只对图片生成缩略图 + if (!file.mimeType.startsWith('image/')) { + return res.status(400).json({ error: 'Not an image file' }) + } + + const thumbnailPath = path.join(tempDir, `thumbnail-${fileId}.jpg`) + + // 生成缩略图 + await sharp(file.filePath) + .resize(100, 100, { + fit: 'inside', + withoutEnlargement: true + }) + .jpeg({ quality: 80 }) + .toFile(thumbnailPath) + + res.sendFile(path.resolve(thumbnailPath)) + + } catch (error) { + console.error('Thumbnail generation error:', error) + // 如果缩略图生成失败,返回原图 + res.sendFile(path.resolve(file.filePath)) + } +}) + // 健康检查接口 app.get('/api/health', (req, res) => { res.json({ diff --git a/src/renderer/components/FileList.vue b/src/renderer/components/FileList.vue index 6a944f3..70d0ea7 100644 --- a/src/renderer/components/FileList.vue +++ b/src/renderer/components/FileList.vue @@ -18,6 +18,8 @@ 文件名 大小 + 类型 + OCR状态 MD5 上传时间 操作 @@ -25,11 +27,18 @@ - {{ decodeFileName(file.originalName) }} + + {{ decodeFileName(file.originalName) }} + {{ formatFileSize(file.fileSize) }} + {{ getFileType(file.mimeType) }} + + ✓ 已识别 + 待识别 + {{ file.md5 }} {{ formatDate(file.createdAt) }} - + @@ -79,7 +89,7 @@ @@ -391,6 +708,7 @@ onMounted(async () => { display: flex; flex-direction: column; background: #f8f9fa; + overflow: hidden; /* 容器不滚动 */ } .ocr-header { @@ -400,6 +718,7 @@ onMounted(async () => { display: flex; justify-content: space-between; align-items: center; + flex-shrink: 0; /* 防止头部被压缩 */ } .back-btn { @@ -409,6 +728,7 @@ onMounted(async () => { padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; + transition: all 0.2s; } .back-btn:hover { @@ -427,6 +747,7 @@ onMounted(async () => { border-radius: 4px; cursor: pointer; font-size: 0.875rem; + transition: background-color 0.2s; } .btn:disabled { @@ -439,11 +760,28 @@ onMounted(async () => { color: white; } +.btn-primary:hover:not(:disabled) { + background: #0069d9; +} + +.btn-edit { + background: #ffc107; + color: #212529; +} + +.btn-edit:hover:not(:disabled) { + background: #e0a800; +} + .btn-secondary { background: #6c757d; color: white; } +.btn-secondary:hover:not(:disabled) { + background: #5a6268; +} + .btn-sm { padding: 0.25rem 0.5rem; font-size: 0.75rem; @@ -472,6 +810,7 @@ onMounted(async () => { display: flex; justify-content: space-between; align-items: center; + flex-shrink: 0; /* 防止头部被压缩 */ } .panel-header h3 { @@ -484,9 +823,76 @@ onMounted(async () => { color: #6c757d; } +/* 预览控制按钮 */ +.preview-controls { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.zoom-btn, .reset-btn { + background: #6c757d; + color: white; + border: none; + width: 30px; + height: 30px; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.2s; +} + +.zoom-btn:hover, .reset-btn:hover { + background: #5a6268; +} + +.zoom-level { + font-size: 0.875rem; + min-width: 40px; + text-align: center; +} + +/* 修复图片预览容器 */ +.file-preview-container { + flex: 1; + overflow: hidden; /* 防止内部溢出 */ + display: flex; + flex-direction: column; +} + +.file-preview { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 1rem; + background: #f8f9fa; + overflow: hidden; /* 防止拖拽时出现滚动条 */ + cursor: grab; + position: relative; + min-height: 0; /* 重要:允许内容缩小 */ +} + +.file-preview:active { + cursor: grabbing; +} + +.preview-image { + max-width: 100%; + max-height: 100%; + object-fit: contain; + border: 1px solid #e9ecef; + border-radius: 4px; + transition: transform 0.1s ease; + transform-origin: center center; +} + .file-list { flex: 1; overflow-y: auto; + min-height: 0; /* 重要:允许内容缩小 */ } .file-item { @@ -495,6 +901,7 @@ onMounted(async () => { padding: 0.75rem; cursor: pointer; border-bottom: 1px solid #f8f9fa; + transition: background-color 0.2s; } .file-item:hover { @@ -513,6 +920,7 @@ onMounted(async () => { display: flex; align-items: center; justify-content: center; + flex-shrink: 0; } .file-thumbnail { @@ -549,45 +957,11 @@ onMounted(async () => { color: #6c757d; } -.file-preview { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - padding: 1rem; - background: #f8f9fa; -} - -.preview-image, -.page-image { - max-width: 100%; - max-height: 100%; - object-fit: contain; -} - -.document-preview { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; -} - -.page-controls { - display: flex; - justify-content: center; - align-items: center; - gap: 1rem; - padding: 1rem; - background: white; - border-bottom: 1px solid #e9ecef; -} - -.page-image-container { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - background: white; +.ocr-indicator { + font-size: 0.7rem; + color: #28a745; + font-weight: bold; + margin-top: 0.25rem; } .file-type-large { @@ -601,10 +975,22 @@ onMounted(async () => { color: #6c757d; } +.file-format { + font-size: 0.875rem; + margin-top: 0.5rem; +} + +.hint { + font-size: 0.875rem; + margin-top: 0.5rem; + color: #6c757d; +} + .ocr-result { flex: 1; overflow-y: auto; padding: 1rem; + min-height: 0; /* 重要:允许内容缩小 */ } .processing { @@ -628,12 +1014,24 @@ onMounted(async () => { 100% { transform: rotate(360deg); } } +.processing-time { + font-size: 0.875rem; + margin-top: 0.5rem; +} + .result-content { line-height: 1.6; } .text-block { margin-bottom: 1rem; + padding: 0.5rem; + border-radius: 4px; + transition: background-color 0.2s; +} + +.text-block:hover { + background-color: #f8f9fa; } .text-content { @@ -641,22 +1039,42 @@ onMounted(async () => { word-wrap: break-word; } +/* 可编辑文本 - 修复滚动问题 */ +.editable-text { + width: 100%; + min-height: 60px; + max-height: 300px; /* 限制最大高度 */ + padding: 0.5rem; + border: 1px solid #007bff; + border-radius: 4px; + font-family: inherit; + font-size: inherit; + resize: vertical; + transition: border-color 0.2s; + overflow-y: auto; /* 确保可以滚动 */ +} + +.editable-text:focus { + outline: none; + border-color: #0056b3; +} + +.static-text { + white-space: pre-wrap; + word-wrap: break-word; +} + .image-block { text-align: center; margin: 1rem 0; -} - -.image-block img { - max-width: 100%; - max-height: 200px; - border: 1px solid #e9ecef; + padding: 0.5rem; + background: #f8f9fa; border-radius: 4px; } .image-caption { font-size: 0.875rem; color: #6c757d; - margin-top: 0.5rem; } .reference-block { @@ -682,6 +1100,11 @@ onMounted(async () => { font-size: 0.75rem; cursor: pointer; margin-top: 0.5rem; + transition: background-color 0.2s; +} + +.search-btn:hover { + background: #6c4d03; } .citation-block { @@ -697,9 +1120,118 @@ onMounted(async () => { margin-right: 0.5rem; } +/* 文本块操作 */ +.block-actions { + display: flex; + gap: 0.5rem; + margin-top: 0.5rem; +} + +.btn-delete { + background: #dc3545; + color: white; + border: none; + padding: 0.25rem 0.5rem; + border-radius: 3px; + font-size: 0.75rem; + cursor: pointer; + transition: background-color 0.2s; +} + +.btn-delete:hover { + background: #c82333; +} + +.btn-add { + background: #28a745; + color: white; + border: none; + padding: 0.25rem 0.5rem; + border-radius: 3px; + font-size: 0.75rem; + cursor: pointer; + transition: background-color 0.2s; +} + +.btn-add:hover { + background: #218838; +} + +/* 添加文本块区域 */ +.add-block-section { + margin: 1rem 0; + text-align: center; +} + +.btn-add-block { + background: #17a2b8; + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.2s; +} + +.btn-add-block:hover { + background: #138496; +} + .no-result { text-align: center; padding: 2rem; color: #6c757d; } + +.result-summary { + margin-top: 2rem; + padding-top: 1rem; + border-top: 1px solid #e9ecef; + font-size: 0.875rem; + color: #6c757d; +} + +.confidence { + font-size: 0.75rem; + color: #28a745; + font-weight: bold; +} + +/* 校对标记 */ +.corrected-badge { + background: #28a745; + color: white; + padding: 0.1rem 0.4rem; + border-radius: 10px; + font-size: 0.7rem; + margin-left: 0.5rem; +} + +.corrected-info { + color: #28a745; + font-weight: bold; +} + +/* 响应式设计 */ +@media (max-width: 1200px) { + .ocr-content { + grid-template-columns: 250px 1fr 350px; + } +} + +@media (max-width: 768px) { + .ocr-content { + grid-template-columns: 1fr; + grid-template-rows: 200px 1fr 400px; + } + + .file-panel, .detail-panel, .result-panel { + min-height: 0; + } + + .header-actions { + flex-direction: column; + gap: 0.5rem; + } +} \ No newline at end of file diff --git a/src/renderer/views/Home.vue b/src/renderer/views/Home.vue index bc8d388..678caf6 100644 --- a/src/renderer/views/Home.vue +++ b/src/renderer/views/Home.vue @@ -90,6 +90,8 @@ const handleOcrRecognize = (file: FileRecord): void => { console.log('导航成功:', res) }).catch((error) => { console.error('导航错误:', error) + // 如果路由跳转失败,尝试使用 hash 方式 + window.location.hash = `/ocr?fileId=${file.id}` }) } @@ -104,6 +106,8 @@ const handleOcrRecognize = (file: FileRecord): void => { body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; + height: 100vh; + overflow: hidden; } #app { @@ -120,6 +124,7 @@ body { justify-content: space-between; align-items: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1); + flex-shrink: 0; /* 防止头部被压缩 */ } .network-indicator { @@ -143,6 +148,6 @@ body { display: flex; flex-direction: column; gap: 1rem; - overflow: hidden; + overflow: hidden; /* 改为 hidden,内部组件自己处理滚动 */ } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 33019ee..e1010f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,6 +42,13 @@ optionalDependencies: global-agent "^3.0.0" +"@emnapi/runtime@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.0.tgz#d7ef3832df8564fe5903bf0567aedbd19538ecbe" + integrity sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q== + dependencies: + tslib "^2.4.0" + "@epic-web/invariant@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@epic-web/invariant/-/invariant-1.0.0.tgz#1073e5dee6dd540410784990eb73e4acd25c9813" @@ -216,6 +223,153 @@ dependencies: "@hapi/hoek" "^11.0.2" +"@img/colour@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311" + integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw== + +"@img/sharp-darwin-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz#6e0732dcade126b6670af7aa17060b926835ea86" + integrity sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.2.4" + +"@img/sharp-darwin-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz#19bc1dd6eba6d5a96283498b9c9f401180ee9c7b" + integrity sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.2.4" + +"@img/sharp-libvips-darwin-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz#2894c0cb87d42276c3889942e8e2db517a492c43" + integrity sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g== + +"@img/sharp-libvips-darwin-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz#e63681f4539a94af9cd17246ed8881734386f8cc" + integrity sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg== + +"@img/sharp-libvips-linux-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz#b1b288b36864b3bce545ad91fa6dadcf1a4ad318" + integrity sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw== + +"@img/sharp-libvips-linux-arm@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz#b9260dd1ebe6f9e3bdbcbdcac9d2ac125f35852d" + integrity sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A== + +"@img/sharp-libvips-linux-ppc64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz#4b83ecf2a829057222b38848c7b022e7b4d07aa7" + integrity sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA== + +"@img/sharp-libvips-linux-riscv64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz#880b4678009e5a2080af192332b00b0aaf8a48de" + integrity sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA== + +"@img/sharp-libvips-linux-s390x@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz#74f343c8e10fad821b38f75ced30488939dc59ec" + integrity sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ== + +"@img/sharp-libvips-linux-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce" + integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw== + +"@img/sharp-libvips-linuxmusl-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz#c8d6b48211df67137541007ee8d1b7b1f8ca8e06" + integrity sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw== + +"@img/sharp-libvips-linuxmusl-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz#be11c75bee5b080cbee31a153a8779448f919f75" + integrity sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg== + +"@img/sharp-linux-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz#7aa7764ef9c001f15e610546d42fce56911790cc" + integrity sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.2.4" + +"@img/sharp-linux-arm@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz#5fb0c3695dd12522d39c3ff7a6bc816461780a0d" + integrity sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.2.4" + +"@img/sharp-linux-ppc64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz#9c213a81520a20caf66978f3d4c07456ff2e0813" + integrity sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA== + optionalDependencies: + "@img/sharp-libvips-linux-ppc64" "1.2.4" + +"@img/sharp-linux-riscv64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz#cdd28182774eadbe04f62675a16aabbccb833f60" + integrity sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw== + optionalDependencies: + "@img/sharp-libvips-linux-riscv64" "1.2.4" + +"@img/sharp-linux-s390x@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz#93eac601b9f329bb27917e0e19098c722d630df7" + integrity sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.2.4" + +"@img/sharp-linux-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8" + integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.2.4" + +"@img/sharp-linuxmusl-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz#d6515ee971bb62f73001a4829b9d865a11b77086" + integrity sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.2.4" + +"@img/sharp-linuxmusl-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz#d97978aec7c5212f999714f2f5b736457e12ee9f" + integrity sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.2.4" + +"@img/sharp-wasm32@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz#2f15803aa626f8c59dd7c9d0bbc766f1ab52cfa0" + integrity sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw== + dependencies: + "@emnapi/runtime" "^1.7.0" + +"@img/sharp-win32-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz#3706e9e3ac35fddfc1c87f94e849f1b75307ce0a" + integrity sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g== + +"@img/sharp-win32-ia32@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz#0b71166599b049e032f085fb9263e02f4e4788de" + integrity sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg== + +"@img/sharp-win32-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz#a81ffb00e69267cd0a1d626eaedb8a8430b2b2f8" + integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw== + "@jridgewell/sourcemap-codec@^1.5.5": version "1.5.5" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" @@ -762,6 +916,11 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +bmp-js@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" + integrity sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw== + body-parser@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa" @@ -878,6 +1037,14 @@ call-bound@^1.0.2: call-bind-apply-helpers "^1.0.2" get-intrinsic "^1.3.0" +canvas@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-3.2.0.tgz#877c51aabdb99cbb5b2b378138a6cdd681e9d390" + integrity sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA== + dependencies: + node-addon-api "^7.0.0" + prebuild-install "^7.1.3" + chalk@4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -1089,7 +1256,7 @@ depd@2.0.0, depd@^2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -detect-libc@^2.0.0: +detect-libc@^2.0.0, detect-libc@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== @@ -1611,6 +1778,11 @@ iconv-lite@^0.6.2, iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +idb-keyval@^6.2.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-6.2.2.tgz#b0171b5f73944854a3291a5cdba8e12768c4854a" + integrity sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg== + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -1674,6 +1846,11 @@ is-promise@^4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1975,6 +2152,13 @@ node-addon-api@^7.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== +node-fetch@^2.6.9: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-gyp@8.x: version "8.4.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" @@ -1991,6 +2175,11 @@ node-gyp@8.x: tar "^6.1.2" which "^2.0.2" +node-tesseract-ocr@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-tesseract-ocr/-/node-tesseract-ocr-2.2.1.tgz#465fea1a1acd6720efb582d5f6c2bcbb82cce94d" + integrity sha512-Q9cD79JGpPNQBxbi1fV+OAsTxYKLpx22sagsxSyKbu1u+t6UarApf5m32uVc8a5QAP1Wk7fIPN0aJFGGEE9DyQ== + nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -2042,6 +2231,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + p-cancelable@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" @@ -2103,7 +2297,7 @@ postcss@^8.5.6: picocolors "^1.1.1" source-map-js "^1.2.1" -prebuild-install@^7.1.1: +prebuild-install@^7.1.1, prebuild-install@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec" integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug== @@ -2206,6 +2400,11 @@ readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable string_decoder "^1.1.1" util-deprecate "^1.0.1" +regenerator-runtime@^0.13.3: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -2316,7 +2515,7 @@ semver@^6.2.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5: +semver@^7.3.2, semver@^7.3.5, semver@^7.7.3: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -2365,6 +2564,40 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sharp@^0.34.5: + version "0.34.5" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" + integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== + dependencies: + "@img/colour" "^1.0.0" + detect-libc "^2.1.2" + semver "^7.7.3" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.34.5" + "@img/sharp-darwin-x64" "0.34.5" + "@img/sharp-libvips-darwin-arm64" "1.2.4" + "@img/sharp-libvips-darwin-x64" "1.2.4" + "@img/sharp-libvips-linux-arm" "1.2.4" + "@img/sharp-libvips-linux-arm64" "1.2.4" + "@img/sharp-libvips-linux-ppc64" "1.2.4" + "@img/sharp-libvips-linux-riscv64" "1.2.4" + "@img/sharp-libvips-linux-s390x" "1.2.4" + "@img/sharp-libvips-linux-x64" "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.2.4" + "@img/sharp-libvips-linuxmusl-x64" "1.2.4" + "@img/sharp-linux-arm" "0.34.5" + "@img/sharp-linux-arm64" "0.34.5" + "@img/sharp-linux-ppc64" "0.34.5" + "@img/sharp-linux-riscv64" "0.34.5" + "@img/sharp-linux-s390x" "0.34.5" + "@img/sharp-linux-x64" "0.34.5" + "@img/sharp-linuxmusl-arm64" "0.34.5" + "@img/sharp-linuxmusl-x64" "0.34.5" + "@img/sharp-wasm32" "0.34.5" + "@img/sharp-win32-arm64" "0.34.5" + "@img/sharp-win32-ia32" "0.34.5" + "@img/sharp-win32-x64" "0.34.5" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -2594,6 +2827,26 @@ tar@^6.0.2, tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +tesseract.js-core@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-6.0.0.tgz#6f25da94f70f8e8f02aff47a43be61d49e6f67c3" + integrity sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA== + +tesseract.js@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-6.0.1.tgz#5b2ff39aae92d59cef79589a43a0f3ab963801cc" + integrity sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA== + dependencies: + bmp-js "^0.1.0" + idb-keyval "^6.2.0" + is-url "^1.2.4" + node-fetch "^2.6.9" + opencollective-postinstall "^2.0.3" + regenerator-runtime "^0.13.3" + tesseract.js-core "^6.0.0" + wasm-feature-detect "^1.2.11" + zlibjs "^0.3.1" + tinyglobby@^0.2.15: version "0.2.15" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" @@ -2607,6 +2860,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + tree-kill@1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -2617,7 +2875,7 @@ tslib@^1.7.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0: +tslib@^2.1.0, tslib@^2.4.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -2776,6 +3034,24 @@ wait-on@^9.0.1: minimist "^1.2.8" rxjs "^7.8.2" +wasm-feature-detect@^1.2.11: + version "1.8.0" + resolved "https://registry.yarnpkg.com/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz#4e9f55b0a64d801f372fbb0324ed11ad3abd0c78" + integrity sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2844,3 +3120,8 @@ yauzl@^2.10.0: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +zlibjs@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/zlibjs/-/zlibjs-0.3.1.tgz#50197edb28a1c42ca659cc8b4e6a9ddd6d444554" + integrity sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==