修复无法进行离线 OCR 的问题

This commit is contained in:
锦恢 2025-05-02 20:00:06 +08:00
parent 56145bfdf9
commit e206b502b5
22 changed files with 97 additions and 20 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ resources
.exe .exe
.idea .idea
*.traineddata

View File

@ -23,3 +23,4 @@ software/**
.editorconfig .editorconfig
.gitattributes .gitattributes
*.vsix

View File

@ -12,7 +12,7 @@
## OpenMCP ## OpenMCP
一款用于 MCP 服务端调试的一体化 vscode/trae 插件。 一款用于 MCP 服务端调试的一体化 vscode/trae/cursor 插件。
集成 Inspector + MCP 客户端基础功能,开发测试一体化。 集成 Inspector + MCP 客户端基础功能,开发测试一体化。

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "openmcp", "name": "openmcp",
"version": "0.0.5", "version": "0.0.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "openmcp", "name": "openmcp",
"version": "0.0.5", "version": "0.0.6",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.10.2", "@modelcontextprotocol/sdk": "^1.10.2",
"@seald-io/nedb": "^4.1.1", "@seald-io/nedb": "^4.1.1",

View File

@ -97,6 +97,12 @@
"title": "打开配置", "title": "打开配置",
"category": "openmcp", "category": "openmcp",
"icon": "$(gear)" "icon": "$(gear)"
},
{
"command": "openmcp.hook.test-ocr",
"title": "测试 OCR",
"category": "openmcp",
"icon": "$(test)"
} }
], ],
"menus": { "menus": {

View File

@ -6,7 +6,7 @@
<div class="message-role"> <div class="message-role">
Agent Agent
<span class="message-reminder"> <span class="message-reminder">
正在生成答案 {{ t('generate-answer') }}
<span class="tool-loading iconfont icon-double-loading"> <span class="tool-loading iconfont icon-double-loading">
</span> </span>
</span> </span>
@ -19,8 +19,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from 'vue'; import { defineProps } from 'vue';
import { useI18n } from 'vue-i18n';
import { markdownToHtml } from '../markdown/markdown'; import { markdownToHtml } from '../markdown/markdown';
const { t } = useI18n();
const props = defineProps({ const props = defineProps({
streamingContent: { streamingContent: {
type: String, type: String,

View File

@ -9,7 +9,8 @@
<el-dialog v-model="showSystemPromptDialog" :title="t('system-prompt')" width="600px"> <el-dialog v-model="showSystemPromptDialog" :title="t('system-prompt')" width="600px">
<div v-if="!showAdd"> <div v-if="!showAdd">
<el-select v-model="tabStorage.settings.systemPrompt" placeholder="选择预设" <el-select v-model="tabStorage.settings.systemPrompt"
:placeholder="t('choose-presetting')"
style="width: 100%; margin-bottom: 20px;"> style="width: 100%; margin-bottom: 20px;">
<el-option v-for="prompt in systemPrompts" <el-option v-for="prompt in systemPrompts"
@ -59,10 +60,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, inject, watch, onMounted } from 'vue'; import { ref, computed, inject, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ChatStorage } from '../chat'; import { ChatStorage } from '../chat';
import { systemPrompts, saveSystemPrompts, setSystemPrompt, loadSystemPrompts, deleteSystemPrompt } from './system-prompt'; import { systemPrompts, setSystemPrompt, loadSystemPrompts, deleteSystemPrompt } from './system-prompt';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { debounce } from 'lodash'; import { debounce } from 'lodash';

View File

@ -147,5 +147,7 @@
"connect-sigature": "توقيع الاتصال", "connect-sigature": "توقيع الاتصال",
"finish-refresh": "تم التحديث", "finish-refresh": "تم التحديث",
"add-system-prompt.name-placeholder": "عنوان prompt المخصص", "add-system-prompt.name-placeholder": "عنوان prompt المخصص",
"enter-message-dot": "أدخل الرسالة..." "enter-message-dot": "أدخل الرسالة...",
"generate-answer": "جارٍ إنشاء الإجابة",
"choose-presetting": "اختر الإعداد المسبق"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "Verbindungssignatur", "connect-sigature": "Verbindungssignatur",
"finish-refresh": "Aktualisierung abgeschlossen", "finish-refresh": "Aktualisierung abgeschlossen",
"add-system-prompt.name-placeholder": "Titel für benutzerdefinierte Eingabeaufforderung", "add-system-prompt.name-placeholder": "Titel für benutzerdefinierte Eingabeaufforderung",
"enter-message-dot": "Nachricht eingeben..." "enter-message-dot": "Nachricht eingeben...",
"generate-answer": "Antwort wird generiert",
"choose-presetting": "Voreinstellung auswählen"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "Connection signature", "connect-sigature": "Connection signature",
"finish-refresh": "Refresh completed", "finish-refresh": "Refresh completed",
"add-system-prompt.name-placeholder": "Title for custom prompt", "add-system-prompt.name-placeholder": "Title for custom prompt",
"enter-message-dot": "Enter message..." "enter-message-dot": "Enter message...",
"generate-answer": "Generating answer",
"choose-presetting": "Select preset"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "Signature de connexion", "connect-sigature": "Signature de connexion",
"finish-refresh": "Actualisation terminée", "finish-refresh": "Actualisation terminée",
"add-system-prompt.name-placeholder": "Titre de l'invite personnalisée", "add-system-prompt.name-placeholder": "Titre de l'invite personnalisée",
"enter-message-dot": "Entrez un message..." "enter-message-dot": "Entrez un message...",
"generate-answer": "Génération de la réponse",
"choose-presetting": "Sélectionner un préréglage"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "接続署名", "connect-sigature": "接続署名",
"finish-refresh": "更新が完了しました", "finish-refresh": "更新が完了しました",
"add-system-prompt.name-placeholder": "カスタムプロンプトのタイトル", "add-system-prompt.name-placeholder": "カスタムプロンプトのタイトル",
"enter-message-dot": "メッセージを入力..." "enter-message-dot": "メッセージを入力...",
"generate-answer": "回答を生成中",
"choose-presetting": "プリセットを選択"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "연결 서명", "connect-sigature": "연결 서명",
"finish-refresh": "새로 고침 완료", "finish-refresh": "새로 고침 완료",
"add-system-prompt.name-placeholder": "사용자 지정 프롬프트 제목", "add-system-prompt.name-placeholder": "사용자 지정 프롬프트 제목",
"enter-message-dot": "메시지를 입력하세요..." "enter-message-dot": "메시지를 입력하세요...",
"generate-answer": "답변 생성 중",
"choose-presetting": "프리셋 선택"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "Подпись соединения", "connect-sigature": "Подпись соединения",
"finish-refresh": "Обновление завершено", "finish-refresh": "Обновление завершено",
"add-system-prompt.name-placeholder": "Заголовок пользовательского prompt", "add-system-prompt.name-placeholder": "Заголовок пользовательского prompt",
"enter-message-dot": "Введите сообщение..." "enter-message-dot": "Введите сообщение...",
"generate-answer": "Генерация ответа",
"choose-presetting": "Выбрать预设"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "连接签名", "connect-sigature": "连接签名",
"finish-refresh": "完成刷新", "finish-refresh": "完成刷新",
"add-system-prompt.name-placeholder": "输入自定义 prompt 的标题", "add-system-prompt.name-placeholder": "输入自定义 prompt 的标题",
"enter-message-dot": "输入消息..." "enter-message-dot": "输入消息...",
"generate-answer": "正在生成答案",
"choose-presetting": "选择预设"
} }

View File

@ -147,5 +147,7 @@
"connect-sigature": "連接簽名", "connect-sigature": "連接簽名",
"finish-refresh": "刷新完成", "finish-refresh": "刷新完成",
"add-system-prompt.name-placeholder": "自定義提示的標題", "add-system-prompt.name-placeholder": "自定義提示的標題",
"enter-message-dot": "輸入訊息..." "enter-message-dot": "輸入訊息...",
"generate-answer": "正在生成答案",
"choose-presetting": "選擇預設"
} }

View File

@ -1,5 +1,10 @@
export let VSCODE_WORKSPACE = ''; export let VSCODE_WORKSPACE = '';
export let RUNNING_CWD = '';
export function setVscodeWorkspace(workspace: string) { export function setVscodeWorkspace(workspace: string) {
VSCODE_WORKSPACE = workspace; VSCODE_WORKSPACE = workspace;
} }
export function setRunningCWD(path: string) {
RUNNING_CWD = path;
}

View File

@ -1,5 +1,5 @@
export { routeMessage } from './common/router'; export { routeMessage } from './common/router';
export { VSCodeWebViewLike } from './hook/adapter'; export { VSCodeWebViewLike } from './hook/adapter';
export { setVscodeWorkspace } from './hook/setting'; export { setVscodeWorkspace, setRunningCWD } from './hook/setting';
// TODO: 更加规范 // TODO: 更加规范
export { client } from './mcp/connect.service'; export { client } from './mcp/connect.service';

View File

@ -6,6 +6,7 @@ import { diskStorage, ocrDB } from '../hook/db';
import * as fs from 'fs'; import * as fs from 'fs';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import { RUNNING_CWD } from '../hook/setting';
export const ocrWorkerStorage = new Map<string, OcrWorker>(); export const ocrWorkerStorage = new Map<string, OcrWorker>();
@ -46,12 +47,17 @@ export async function tesseractOCR(
logger: (message: Tesseract.LoggerMessage) => void, logger: (message: Tesseract.LoggerMessage) => void,
lang: string = 'eng+chi_sim' lang: string = 'eng+chi_sim'
) { ) {
try { try {
const { data: { text } } = await Tesseract.recognize( const { data: { text } } = await Tesseract.recognize(
imagePath, imagePath,
lang, lang,
{ {
logger logger,
langPath: './',
gzip: false,
cacheMethod: 'cache',
cachePath: RUNNING_CWD
} }
); );
@ -67,6 +73,10 @@ export function createOcrWorker(filename: string, webview: PostMessageble): OcrW
const workerId = uuidv4(); const workerId = uuidv4();
const logger = (message: Tesseract.LoggerMessage) => { const logger = (message: Tesseract.LoggerMessage) => {
console.log('report ocr status');
console.log(message);
webview.postMessage({ webview.postMessage({
command: 'ocr/worker/log', command: 'ocr/worker/log',
data: { data: {

View File

@ -4,12 +4,14 @@ import { HelpProvider } from '../sidebar/help.controller';
import { McpWorkspaceConnectProvider } from '../sidebar/workspace.controller'; import { McpWorkspaceConnectProvider } from '../sidebar/workspace.controller';
import { McpInstalledConnectProvider } from '../sidebar/installed.controller'; import { McpInstalledConnectProvider } from '../sidebar/installed.controller';
import { WebviewController } from '../webview/webview.controller'; import { WebviewController } from '../webview/webview.controller';
import { HookController } from '../hook/hook.controller';
export const InstallModules = [ export const InstallModules = [
McpWorkspaceConnectProvider, McpWorkspaceConnectProvider,
McpInstalledConnectProvider, McpInstalledConnectProvider,
HelpProvider, HelpProvider,
WebviewController WebviewController,
HookController
]; ];
const registerSingles = new Map<string, any>(); const registerSingles = new Map<string, any>();

View File

@ -9,7 +9,9 @@ export function activate(context: vscode.ExtensionContext) {
// 获取当前打开的项目的路径 // 获取当前打开的项目的路径
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
const workspace = workspaceFolder?.uri.fsPath || ''; const workspace = workspaceFolder?.uri.fsPath || '';
OpenMCPService.setVscodeWorkspace(workspace); OpenMCPService.setVscodeWorkspace(workspace);
OpenMCPService.setRunningCWD(context.extensionPath);
launch(context); launch(context);
} }

View File

@ -0,0 +1,28 @@
import { RegisterCommand } from "../common";
import * as vscode from 'vscode';
import * as path from 'path';
import Tesseract from 'tesseract.js';
export class HookController {
@RegisterCommand('openmcp.hook.test-ocr')
async testOcr(context: vscode.ExtensionContext) {
const testImage = path.join(context.extensionPath, 'icons/openmcp.resource.png');
const { data: { text } } = await Tesseract.recognize(
testImage,
'eng+chi_sim',
{
logger: (m) => console.log(m),
langPath: './',
gzip: false,
cacheMethod: 'cache',
cachePath: context.extensionPath
}
);
vscode.window.showInformationMessage('ocr result: ' + text);
}
}