\n
\n
模型设置
\n \n ${i?`\n
\n ✅ 已上传模型: ${(n.model.byteLength/1024/1024).toFixed(2)} MB\n
\n `:""}\n \n
\n
\n
使用上传的模型
\n
上传 common.onnx 和 charsets.json
\n
\n
\n
\n
\n
\n
未选择文件
\n
\n
未选择文件
\n
\n \n \n
\n
\n
\n
\n
自动下载模型
\n
首次使用时自动下载模型文件
\n
\n
\n
\n
\n
\n
功能设置
\n
\n
\n
自动检测并填充验证码
\n
自动识别页面中的验证码并填充
\n
\n
\n
\n
\n
\n
验证码选择器
\n
CSS选择器,留空则自动检测
\n
\n
\n
\n
\n
\n
输入框选择器
\n
CSS选择器,留空则自动查找
\n
\n
\n
\n
\n
\n
站点白名单
\n
\n
\n
\n
\n 支持通配符 * 匹配子域名。当前站点:${window.location.hostname}\n
\n
\n
\n
\n \n \n
\n
\n `,document.body.appendChild(this.container),this.bindEvents();}bindEvents(){this.container&&(this.container.querySelector(".ddddocr-settings-close")?.addEventListener("click",()=>{this.hide();}),this.container.querySelectorAll(".ddddocr-switch").forEach(t=>{t.addEventListener("click",t=>{const e=t.currentTarget,n=e.dataset.setting,i=e.classList.toggle("active");if("useUploadedModel"===n){const t=this.container.querySelector("#uploadModelArea"),e=this.container.querySelector("#autoDownloadItem");t.style.display=i?"block":"none",e.style.display=i?"none":"flex";}"enableWhitelist"===n&&(this.container.querySelector("#whitelistSettings").style.display=i?"block":"none");});}),this.container.querySelector("#modelFileInput")?.addEventListener("change",t=>{const e=t.target.files?.[0],n=this.container.querySelector("#modelFileName");e&&(n.textContent=`✅ ${e.name} (${(e.size/1024/1024).toFixed(2)} MB)`);}),this.container.querySelector("#charsetsFileInput")?.addEventListener("change",t=>{const e=t.target.files?.[0],n=this.container.querySelector("#charsetsFileName");e&&(n.textContent=`✅ ${e.name}`);}),this.container.querySelector("#uploadModelBtn")?.addEventListener("click",async()=>{const t=this.container.querySelector("#modelFileInput").files?.[0],e=this.container.querySelector("#charsetsFileInput").files?.[0];if(t&&e)try{await async function(t,e){const n=await t.arrayBuffer(),i=await e.text(),o=JSON.parse(i),s=new ModelCache;await s.setUploadedModel(n,o);}(t,e),saveConfig({useUploadedModel:!0}),c.show({title:"上传成功",content:"模型文件已保存,请刷新页面以应用",icon:"✅"});}catch(n){c.show({title:"上传失败",content:String(n),icon:"❌"});}else c.show({title:"缺少文件",content:"请选择模型文件和字符集文件",icon:"⚠️"});}),this.container.querySelector("#deleteUploadedBtn")?.addEventListener("click",()=>{c.confirm({title:"删除模型",content:"确定要删除已上传的模型吗?",icon:"🗑️",confirmText:"确定删除",cancelText:"取消",onConfirm:async()=>{try{await async function(){const t=new ModelCache;await t.deleteUploadedModel();}(),saveConfig({useUploadedModel:!1}),c.show({title:"删除成功",content:"已删除上传的模型",icon:"✅"}),this.hide();}catch(t){c.show({title:"删除失败",content:String(t),icon:"❌"});}}});}),this.container.querySelector("#saveSettingsBtn")?.addEventListener("click",()=>{this.saveSettings();}),this.container.querySelector("#resetSettingsBtn")?.addEventListener("click",()=>{c.confirm({title:"重置设置",content:"确定要重置所有设置吗?页面将自动刷新。",icon:"⚠️",confirmText:"确定重置",cancelText:"取消",onConfirm:()=>{this.resetSettings();}});}));}saveSettings(){if(!this.container)return;const t={};this.container.querySelectorAll(".ddddocr-switch").forEach(e=>{const n=e.dataset.setting;n&&(t[n]=e.classList.contains("active"));}),this.container.querySelectorAll("input[data-setting]").forEach(e=>{const n=e.dataset.setting;n&&(t[n]=e.value);}),this.container.querySelectorAll("textarea[data-setting]").forEach(e=>{if("whitelist"===e.dataset.setting){const n=e.value;t.whitelist=n.split("\n").filter(t=>t.trim());}}),saveConfig(t),this.onConfigChange(getConfig()),"undefined"!=typeof GM_notification&&GM_notification({title:"设置已保存",text:"配置已成功保存",timeout:2e3}),this.hide();}resetSettings(){saveConfig({autoDetect:false,captchaSelector:"",inputSelector:"",useUploadedModel:false,autoDownload:true,enableWhitelist:false,whitelist:[]}),this.hide(),window.location.reload();}async show(){this.container||await this.createContainer(),this.isVisible=true,this.container.classList.add("ddddocr-settings-visible");}hide(){this.container&&(this.isVisible=false,this.container.classList.remove("ddddocr-settings-visible"));}toggle(){this.isVisible?this.hide():this.show();}setOnConfigChange(t){this.onConfigChange=t;}}class LoadingIndicator{constructor(){this.container=null,this.isVisible=false,this.create();}create(){const t=document.createElement("style");t.textContent="\n .ddddocr-loading-indicator {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 4px;\n z-index: 2147483647;\n overflow: hidden;\n background: linear-gradient(90deg, \n #4A90E2 0%, \n #FF69B4 25%, \n #87CEEB 50%, \n #FF69B4 75%, \n #4A90E2 100%);\n background-size: 200% 100%;\n animation: ddddocr-wave 3s linear infinite;\n opacity: 0;\n transition: opacity 0.3s;\n }\n\n .ddddocr-loading-indicator.visible {\n opacity: 1;\n }\n\n @keyframes ddddocr-wave {\n 0% {\n background-position: 0% 50%;\n }\n 100% {\n background-position: 200% 50%;\n }\n }\n\n .ddddocr-loading-text {\n position: fixed;\n top: 8px;\n left: 50%;\n transform: translateX(-50%);\n background: #4A90E2;\n color: white;\n padding: 6px 16px;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 500;\n z-index: 2147483647;\n opacity: 0;\n transition: opacity 0.3s;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n }\n\n .ddddocr-loading-text.visible {\n opacity: 1;\n }\n ",document.head.appendChild(t),this.container=document.createElement("div"),this.container.className="ddddocr-loading-indicator",document.body.appendChild(this.container);const e=document.createElement("div");e.className="ddddocr-loading-text",e.id="ddddocr-loading-text",e.textContent="正在初始化 DDDD OCR...",document.body.appendChild(e);}show(t){if(!this.container)return;this.isVisible=true,this.container.classList.add("visible");const e=document.getElementById("ddddocr-loading-text");e&&(t&&(e.textContent=t),e.classList.add("visible"));}updateText(t){const e=document.getElementById("ddddocr-loading-text");e&&(e.textContent=t);}hide(){if(!this.container)return;this.isVisible=false,this.container.classList.remove("visible");const t=document.getElementById("ddddocr-loading-text");t&&t.classList.remove("visible"),setTimeout(()=>{this.container?.remove(),t?.remove(),this.container=null;},300);}}class EventEmitter{constructor(){this.listeners=new Map;}on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e);}off(t,e){this.listeners.get(t)?.delete(e);}emit(t,e){this.listeners.get(t)?.forEach(t=>{try{t(e);}catch(n){}});}clear(){this.listeners.clear();}}class OCRApp{constructor(){this.loadingIndicator=null,this.initialized=false,this.eventEmitter=new EventEmitter,this.ocr=new DdddOCR,this.detector=new AutoDetector(this.ocr,this.eventEmitter),this.settingsUI=new SettingsUI,this.registerMenuCommands(),this.settingsUI.setOnConfigChange(t=>{this.handleConfigChange(t);});}async init(){if(!shouldExecuteScript())return;if(this.initialized)return;const t=getConfig();this.initialized=true,this.loadingIndicator=new LoadingIndicator;try{this.loadingIndicator.show("正在初始化 DDDD OCR"),this.loadingIndicator.updateText("正在加载模型文件"),await this.ocr.init(),this.loadingIndicator.updateText("DDDD OCR 已就绪"),t.autoDetect&&this.detector.start(),setTimeout(()=>{this.loadingIndicator?.hide();},2e3),this.showNotification("DDDD OCR 已就绪",t.autoDetect?"自动检测已启用":"点击菜单启用自动检测");}catch(e){this.loadingIndicator&&(this.loadingIndicator.updateText("初始化失败: "+String(e)),setTimeout(()=>{this.loadingIndicator?.hide();},3e3)),this.showNotification("初始化失败",String(e),true);}}registerMenuCommands(){GM_registerMenuCommand("⚙️ 打开设置",()=>this.settingsUI.show(),"s"),GM_registerMenuCommand("🤖 切换自动检测",()=>this.toggleAutoDetect(),"a"),GM_registerMenuCommand("🗑️ 清除所有缓存",async()=>{c.confirm({title:"清除缓存",content:"确定要清除所有缓存吗(包括模型和 WASM)?下次启动将重新下载。",icon:"🗑️",confirmText:"确定清除",cancelText:"取消",onConfirm:async()=>{await async function(){const t=new ModelCache;await t.delete();}(),await async function(){await a.clear();}(),this.showNotification("缓存已清除","请刷新页面");}});},"d"),GM_registerMenuCommand("ℹ️ 查看状态",()=>this.showStatus(),"i");}showStatus(){const t=getConfig(),e=isWhitelisted();let n=`\n