这个提交包含在:
徐勤民 2025-11-11 16:36:30 +08:00
父节点 9e8571402d
当前提交 e2e7bb066b
共有 7 个文件被更改,包括 1639 次插入163 次删除

235
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 }

查看文件

@ -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"
},

查看文件

@ -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({

查看文件

@ -18,6 +18,8 @@
<tr>
<th>文件名</th>
<th>大小</th>
<th>类型</th>
<th>OCR状态</th>
<th>MD5</th>
<th>上传时间</th>
<th>操作</th>
@ -25,11 +27,18 @@
</thead>
<tbody>
<tr v-for="file in files" :key="file.id">
<td class="filename-cell">{{ decodeFileName(file.originalName) }}</td>
<td class="filename-cell" :title="decodeFileName(file.originalName)">
{{ decodeFileName(file.originalName) }}
</td>
<td>{{ formatFileSize(file.fileSize) }}</td>
<td class="file-type">{{ getFileType(file.mimeType) }}</td>
<td class="ocr-status">
<span v-if="file.hasOcrResult" class="ocr-done"> 已识别</span>
<span v-else class="ocr-pending">待识别</span>
</td>
<td class="md5-cell">{{ file.md5 }}</td>
<td>{{ formatDate(file.createdAt) }}</td>
<td>
<td class="action-buttons">
<button
@click="checkFileMD5(file)"
class="check-button"
@ -38,9 +47,10 @@
{{ checkingFileId === file.id ? '检查中...' : '检查MD5' }}
</button>
<button
class="btn-ocr"
class="ocr-button"
@click="handleOcr(file)"
title="OCR识别"
:disabled="!file.mimeType.startsWith('image/')"
>
🔍 OCR
</button>
@ -79,7 +89,7 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import apiManager from '../utils/apiManager.ts'
import apiManager from '../utils/apiManager'
interface FileRecord {
id: number
@ -91,6 +101,7 @@ interface FileRecord {
md5: string
createdAt: string
updatedAt: string
hasOcrResult?: boolean
}
interface PaginationInfo {
@ -100,9 +111,11 @@ interface PaginationInfo {
totalPages: number
}
interface FileListResponse {
files: FileRecord[]
interface ApiResponse {
success: boolean
data: FileRecord[]
pagination: PaginationInfo
error?: string
}
interface MD5CheckResponse {
@ -128,24 +141,26 @@ const emit = defineEmits<{
}>()
//
const handleNetworkChange = (event: any) => {
const handleNetworkChange = (event: CustomEvent<{ baseUrl: string; isOnline: boolean }>) => {
isOnline.value = event.detail.isOnline
console.log('文件列表: 网络状态变更', event.detail)
//
loadFiles(1)
}
const handleOcr = (file: FileRecord): void => {
emit('ocrRecognize', file)
}
onMounted(() => {
isOnline.value = apiManager.isOnlineMode()
window.addEventListener('apiBaseUrlChanged', handleNetworkChange)
window.addEventListener('apiBaseUrlChanged', handleNetworkChange as EventListener)
loadFiles()
})
onUnmounted(() => {
window.removeEventListener('apiBaseUrlChanged', handleNetworkChange)
window.removeEventListener('apiBaseUrlChanged', handleNetworkChange as EventListener)
})
//
@ -158,17 +173,55 @@ const decodeFileName = (fileName: string): string => {
}
}
//
const getFileType = (mimeType: string): string => {
if (mimeType.startsWith('image/')) return '图片'
if (mimeType.includes('pdf')) return 'PDF'
if (mimeType.includes('word') || mimeType.includes('document')) return 'Word'
if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return 'Excel'
if (mimeType.includes('powerpoint') || mimeType.includes('presentation')) return 'PPT'
if (mimeType.includes('text')) return '文本'
return '其他'
}
// OCR
const checkOcrStatus = async (file: FileRecord): Promise<boolean> => {
try {
const result = await apiManager.get<{ success: boolean; data: any }>(
`/api/ocr/result/${file.id}`
)
return result.success && result.data !== null
} catch (error) {
return false
}
}
const loadFiles = async (page: number = pagination.value.page): Promise<void> => {
try {
const result: FileListResponse = await apiManager.request(
const result: ApiResponse = await apiManager.request<ApiResponse>(
`/api/files?page=${page}&pageSize=${pagination.value.pageSize}`
)
files.value = result.files
pagination.value = result.pagination
if (result.success && result.data) {
// OCR
const filesWithOcrStatus = await Promise.all(
result.data.map(async (file) => {
const hasOcrResult = await checkOcrStatus(file)
return { ...file, hasOcrResult }
})
)
files.value = filesWithOcrStatus
pagination.value = result.pagination
console.log(`成功加载 ${files.value.length} 个文件`)
} else {
console.error('获取文件列表失败:', result.error)
files.value = []
}
} catch (error) {
console.error('加载文件列表失败:', error)
alert('加载文件列表失败,请检查服务是否正常运行')
files.value = []
}
}
@ -176,7 +229,7 @@ const checkFileMD5 = async (file: FileRecord): Promise<void> => {
checkingFileId.value = file.id
try {
const result: MD5CheckResponse = await apiManager.request(
const result: MD5CheckResponse = await apiManager.request<MD5CheckResponse>(
`/api/files/${file.id}/check-md5`,
{ method: 'POST' }
)
@ -233,6 +286,7 @@ defineExpose({
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden; /* 确保滚动正常工作 */
}
.file-list-header {
@ -240,6 +294,7 @@ defineExpose({
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
flex-shrink: 0; /* 防止头部被压缩 */
}
.header-actions {
@ -255,6 +310,7 @@ defineExpose({
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.refresh-button:hover {
@ -285,19 +341,19 @@ defineExpose({
overflow: auto;
border: 1px solid #ddd;
border-radius: 8px;
background: white;
}
.file-table {
width: 100%;
border-collapse: collapse;
background: white;
}
.file-table th,
.file-table td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #ddd;
border-bottom: 1px solid #eee;
}
.file-table th {
@ -305,22 +361,52 @@ defineExpose({
font-weight: 600;
position: sticky;
top: 0;
color: #495057;
}
.file-table tr:hover {
background-color: #f8f9fa;
}
.filename-cell {
max-width: 250px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.md5-cell {
font-family: monospace;
.file-type {
font-size: 0.875rem;
max-width: 200px;
color: #6c757d;
}
.ocr-status {
font-size: 0.875rem;
}
.ocr-done {
color: #28a745;
font-weight: bold;
}
.ocr-pending {
color: #6c757d;
}
.md5-cell {
font-family: 'Courier New', monospace;
font-size: 0.75rem;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #6c757d;
}
.action-buttons {
display: flex;
gap: 0.5rem;
white-space: nowrap;
}
.check-button {
@ -330,7 +416,8 @@ defineExpose({
padding: 0.25rem 0.5rem;
border-radius: 3px;
cursor: pointer;
font-size: 0.875rem;
font-size: 0.75rem;
transition: background-color 0.2s;
}
.check-button:hover:not(:disabled) {
@ -342,11 +429,35 @@ defineExpose({
cursor: not-allowed;
}
.ocr-button {
background: #3498db;
color: white;
border: none;
padding: 0.25rem 0.5rem;
border-radius: 3px;
cursor: pointer;
font-size: 0.75rem;
transition: background-color 0.2s;
}
.ocr-button:hover:not(:disabled) {
background: #2980b9;
}
.ocr-button:disabled {
background: #bdc3c7;
cursor: not-allowed;
opacity: 0.5;
}
.empty-state {
text-align: center;
padding: 2rem;
color: #666;
padding: 3rem;
color: #6c757d;
font-style: italic;
background: white;
border-radius: 8px;
margin: 1rem 0;
}
.pagination {
@ -356,6 +467,9 @@ defineExpose({
gap: 1rem;
margin-top: 1rem;
padding: 1rem;
background: white;
border-radius: 8px;
flex-shrink: 0; /* 防止分页被压缩 */
}
.page-button {
@ -365,6 +479,7 @@ defineExpose({
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.page-button:hover:not(:disabled) {
@ -377,6 +492,7 @@ defineExpose({
}
.page-info {
color: #666;
color: #6c757d;
font-size: 0.875rem;
}
</style>

文件差异内容过多而无法显示 加载差异

查看文件

@ -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}`
})
}
</script>
@ -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,内部组件自己处理滚动 */
}
</style>

289
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==