diff --git a/bun.lockb b/bun.lockb
index a2dd2af..f5c5689 100644
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 84141a8..236c93d 100644
--- a/package.json
+++ b/package.json
@@ -56,6 +56,7 @@
"rehype-mathjax": "4.0.3",
"remark-gfm": "3.0.1",
"remark-math": "5.1.1",
+ "tesseract.js": "^5.1.1",
"turndown": "^7.1.3",
"yt-transcript": "^0.0.2",
"zustand": "^4.5.0"
diff --git a/src/assets/locale/ar/chrome.json b/src/assets/locale/ar/chrome.json
new file mode 100644
index 0000000..2e75af6
--- /dev/null
+++ b/src/assets/locale/ar/chrome.json
@@ -0,0 +1,13 @@
+{
+ "heading": "إعداد Chrome AI",
+ "status": {
+ "label": "تمكين أو تعطيل دعم Chrome AI في مساعد الصفحة"
+ },
+ "error": {
+ "browser_not_supported": "هذا الإصدار من Chrome غير مدعوم من نموذج Gemini Nano. يرجى التحديث إلى الإصدار 127 أو أحدث",
+ "ai_not_supported": "الإعداد chrome://flags/#prompt-api-for-gemini-nano غير مفعل. يرجى تفعيله.",
+ "ai_not_ready": "Gemini Nano غير جاهز بعد؛ تحتاج إلى التحقق مرة أخرى من إعدادات Chrome.",
+ "internal_error": "حدث خطأ داخلي. يرجى المحاولة مرة أخرى لاحقاً."
+ },
+ "errorDescription": "لاستخدام Chrome AI، تحتاج إلى إصدار متصفح أعلى من 127، وهو متوفر حالياً في قنوات Dev و Canary. بعد تنزيل الإصدار المدعوم، اتبع هذه الخطوات:\n\n1. انتقل إلى `chrome://flags/#prompt-api-for-gemini-nano` واختر \"تمكين\".\n2. انتقل إلى `chrome://flags/#optimization-guide-on-device-model` واختر \"EnabledBypassPrefRequirement\".\n3. انتقل إلى `chrome://components`، ابحث عن \"Optimization Guide On Device Model\"، وانقر على \"التحقق من التحديثات\". سيؤدي هذا إلى تنزيل النموذج. إذا لم تر الإعدادات، كرر الخطوتين 1 و 2 وأعد تشغيل المتصفح."
+}
\ No newline at end of file
diff --git a/src/assets/locale/ar/common.json b/src/assets/locale/ar/common.json
new file mode 100644
index 0000000..2f8e349
--- /dev/null
+++ b/src/assets/locale/ar/common.json
@@ -0,0 +1,128 @@
+{
+ "pageAssist": "مساعد الصفحة",
+ "selectAModel": "اختر نموذجًا",
+ "save": "حفظ",
+ "saved": "تم الحفظ",
+ "cancel": "إلغاء",
+ "retry": "إعادة المحاولة",
+ "share": {
+ "tooltip": {
+ "share": "مشاركة"
+ },
+ "modal": {
+ "title": "مشاركة رابط المحادثة"
+ },
+ "form": {
+ "defaultValue": {
+ "name": "مجهول",
+ "title": "محادثة بدون عنوان"
+ },
+ "title": {
+ "label": "عنوان المحادثة",
+ "placeholder": "أدخل عنوان المحادثة",
+ "required": "عنوان المحادثة مطلوب"
+ },
+ "name": {
+ "label": "اسمك",
+ "placeholder": "أدخل اسمك",
+ "required": "اسمك مطلوب"
+ },
+ "btn": {
+ "save": "إنشاء رابط",
+ "saving": "جاري إنشاء الرابط..."
+ }
+ },
+ "notification": {
+ "successGenerate": "تم نسخ الرابط إلى الحافظة",
+ "failGenerate": "فشل في إنشاء الرابط"
+ }
+ },
+ "copyToClipboard": "نسخ إلى الحافظة",
+ "webSearch": "البحث في الويب",
+ "regenerate": "إعادة التوليد",
+ "edit": "تعديل",
+ "delete": "حذف",
+ "saveAndSubmit": "حفظ وإرسال",
+ "editMessage": {
+ "placeholder": "اكتب رسالة..."
+ },
+ "submit": "إرسال",
+ "noData": "لا توجد بيانات",
+ "noHistory": "لا يوجد سجل محادثات",
+ "chatWithCurrentPage": "الدردشة مع الصفحة الحالية",
+ "beta": "تجريبي",
+ "tts": "قراءة بصوت عالٍ",
+ "currentChatModelSettings": "إعدادات نموذج المحادثة الحالي",
+ "modelSettings": {
+ "label": "إعدادات النموذج",
+ "description": "تعيين خيارات النموذج عالمياً لجميع المحادثات",
+ "form": {
+ "keepAlive": {
+ "label": "الإبقاء نشطاً",
+ "help": "يتحكم في المدة التي سيظل فيها النموذج محملاً في الذاكرة بعد الطلب (الافتراضي: 5 دقائق)",
+ "placeholder": "أدخل مدة البقاء نشطاً (مثال: 5م، 10م، 1س)"
+ },
+ "temperature": {
+ "label": "درجة الحرارة",
+ "placeholder": "أدخل قيمة درجة الحرارة (مثال: 0.7، 1.0)"
+ },
+ "numCtx": {
+ "label": "عدد السياقات",
+ "placeholder": "أدخل قيمة عدد السياقات (الافتراضي: 2048)"
+ },
+ "numPredict": {
+ "label": "الحد الأقصى للرموز",
+ "placeholder": "أدخل قيمة الحد الأقصى للرموز (مثال: 2048، 4096)"
+ },
+ "seed": {
+ "label": "البذرة",
+ "placeholder": "أدخل قيمة البذرة (مثال: 1234)",
+ "help": "إمكانية تكرار مخرجات النموذج"
+ },
+ "topK": {
+ "label": "أعلى K",
+ "placeholder": "أدخل قيمة أعلى K (مثال: 40، 100)"
+ },
+ "topP": {
+ "label": "أعلى P",
+ "placeholder": "أدخل قيمة أعلى P (مثال: 0.9، 0.95)"
+ },
+ "useMMap": {
+ "label": "استخدام MMap"
+ },
+ "numGpu": {
+ "label": "عدد وحدات معالجة الرسومات",
+ "placeholder": "أدخل عدد الطبقات لإرسالها إلى وحدة/وحدات معالجة الرسومات"
+ },
+ "systemPrompt": {
+ "label": "موجه النظام المؤقت",
+ "placeholder": "أدخل موجه النظام",
+ "help": "هذه طريقة سريعة لتعيين موجه النظام في المحادثة الحالية، والذي سيتجاوز موجه النظام المحدد إذا كان موجوداً."
+ }
+ },
+ "advanced": "المزيد من إعدادات النموذج"
+ },
+ "copilot": {
+ "summary": "تلخيص",
+ "explain": "شرح",
+ "rephrase": "إعادة صياغة",
+ "translate": "ترجمة",
+ "custom": "مخصص"
+ },
+ "citations": "الاقتباسات",
+ "segmented": {
+ "ollama": "نماذج Ollama",
+ "custom": "نماذج مخصصة"
+ },
+ "downloadCode": "تنزيل الكود",
+ "date": {
+ "pinned": "مثبت",
+ "today": "اليوم",
+ "yesterday": "الأمس",
+ "last7Days": "آخر 7 أيام",
+ "older": "أقدم"
+ },
+ "pin": "تثبيت",
+ "unpin": "إلغاء التثبيت",
+ "generationInfo": "معلومات التوليد"
+}
diff --git a/src/assets/locale/ar/knowledge.json b/src/assets/locale/ar/knowledge.json
new file mode 100644
index 0000000..ae9f6cd
--- /dev/null
+++ b/src/assets/locale/ar/knowledge.json
@@ -0,0 +1,40 @@
+{
+ "addBtn": "إضافة معرفة جديدة",
+ "columns": {
+ "title": "العنوان",
+ "status": "الحالة",
+ "embeddings": "نموذج التضمين",
+ "createdAt": "تاريخ الإنشاء",
+ "action": "الإجراءات"
+ },
+ "expandedColumns": {
+ "name": "الاسم"
+ },
+ "confirm": {
+ "delete": "هل أنت متأكد أنك تريد حذف هذه المعرفة؟"
+ },
+ "deleteSuccess": "تم حذف المعرفة بنجاح",
+ "status": {
+ "pending": "قيد الانتظار",
+ "finished": "مكتمل",
+ "processing": "قيد المعالجة",
+ "failed": "فشل"
+ },
+ "addKnowledge": "إضافة معرفة",
+ "form": {
+ "title": {
+ "label": "عنوان المعرفة",
+ "placeholder": "أدخل عنوان المعرفة",
+ "required": "عنوان المعرفة مطلوب"
+ },
+ "uploadFile": {
+ "label": "رفع ملف",
+ "uploadText": "اسحب وأفلت ملفًا هنا أو انقر للرفع",
+ "uploadHint": "أنواع الملفات المدعومة: .pdf, .csv, .txt, .md, .docx",
+ "required": "الملف مطلوب"
+ },
+ "submit": "إرسال",
+ "success": "تمت إضافة المعرفة بنجاح"
+ },
+ "noEmbeddingModel": "يرجى إضافة نموذج تضمين من صفحة إعدادات RAG أولاً"
+}
\ No newline at end of file
diff --git a/src/assets/locale/ar/openai.json b/src/assets/locale/ar/openai.json
new file mode 100644
index 0000000..58207fd
--- /dev/null
+++ b/src/assets/locale/ar/openai.json
@@ -0,0 +1,90 @@
+{
+ "settings": "واجهة برمجة التطبيقات المتوافقة مع OpenAI",
+ "heading": "واجهة برمجة التطبيقات المتوافقة مع OpenAI",
+ "subheading": "إدارة وتكوين مزودي واجهة برمجة التطبيقات المتوافقة مع OpenAI هنا.",
+ "addBtn": "إضافة مزود",
+ "table": {
+ "name": "اسم المزود",
+ "baseUrl": "عنوان URL الأساسي",
+ "actions": "إجراء"
+ },
+ "modal": {
+ "titleAdd": "إضافة مزود جديد",
+ "name": {
+ "label": "اسم المزود",
+ "required": "اسم المزود مطلوب.",
+ "placeholder": "أدخل اسم المزود"
+ },
+ "baseUrl": {
+ "label": "عنوان URL الأساسي",
+ "help": "عنوان URL الأساسي لمزود واجهة برمجة التطبيقات OpenAI. مثال (http://localhost:1234/v1)",
+ "required": "عنوان URL الأساسي مطلوب.",
+ "placeholder": "أدخل عنوان URL الأساسي"
+ },
+ "apiKey": {
+ "label": "مفتاح API",
+ "required": "مفتاح API مطلوب.",
+ "placeholder": "أدخل مفتاح API"
+ },
+ "submit": "حفظ",
+ "update": "تحديث",
+ "deleteConfirm": "هل أنت متأكد أنك تريد حذف هذا المزود؟",
+ "model": {
+ "title": "قائمة النماذج",
+ "subheading": "يرجى تحديد نماذج المحادثة التي تريد استخدامها مع هذا المزود.",
+ "success": "تمت إضافة نماذج جديدة بنجاح."
+ },
+ "tipLMStudio": "سيقوم Page Assist تلقائيًا بجلب النماذج التي قمت بتحميلها على LM Studio. لست بحاجة إلى إضافتها يدويًا."
+ },
+ "addSuccess": "تمت إضافة المزود بنجاح.",
+ "deleteSuccess": "تم حذف المزود بنجاح.",
+ "updateSuccess": "تم تحديث المزود بنجاح.",
+ "delete": "حذف",
+ "edit": "تعديل",
+ "newModel": "إضافة نماذج للمزود",
+ "noNewModel": "بالنسبة لـ LMStudio و Ollama و Llamafile، نقوم بالجلب ديناميكيًا. لا حاجة للإضافة اليدوية.",
+ "searchModel": "بحث عن نموذج",
+ "selectAll": "تحديد الكل",
+ "save": "حفظ",
+ "saving": "جاري الحفظ...",
+ "manageModels": {
+ "columns": {
+ "name": "اسم النموذج",
+ "model_type": "نوع النموذج",
+ "model_id": "معرف النموذج",
+ "provider": "اسم المزود",
+ "actions": "إجراء"
+ },
+ "tooltip": {
+ "delete": "حذف"
+ },
+ "confirm": {
+ "delete": "هل أنت متأكد أنك تريد حذف هذا النموذج؟"
+ },
+ "modal": {
+ "title": "إضافة نموذج مخصص",
+ "form": {
+ "name": {
+ "label": "معرف النموذج",
+ "placeholder": "llama3.2",
+ "required": "معرف النموذج مطلوب."
+ },
+ "provider": {
+ "label": "المزود",
+ "placeholder": "اختر المزود",
+ "required": "المزود مطلوب."
+ },
+ "type": {
+ "label": "نوع النموذج"
+ }
+ }
+ }
+ },
+ "noModelFound": "لم يتم العثور على نموذج. تأكد من إضافة المزود الصحيح مع عنوان URL الأساسي ومفتاح API.",
+ "radio": {
+ "chat": "نموذج المحادثة",
+ "embedding": "نموذج التضمين",
+ "chatInfo": "يستخدم لإكمال المحادثة وتوليد المحادثات",
+ "embeddingInfo": "يستخدم لـ RAG ومهام البحث الدلالي الأخرى ذات الصلة."
+ }
+}
\ No newline at end of file
diff --git a/src/assets/locale/ar/option.json b/src/assets/locale/ar/option.json
new file mode 100644
index 0000000..5886434
--- /dev/null
+++ b/src/assets/locale/ar/option.json
@@ -0,0 +1,28 @@
+{
+ "newChat": "محادثة جديدة",
+ "selectAPrompt": "اختر موجهاً",
+ "githubRepository": "مستودع GitHub",
+ "settings": "الإعدادات",
+ "sidebarTitle": "سجل المحادثات",
+ "error": "خطأ",
+ "somethingWentWrong": "حدث خطأ ما",
+ "validationSelectModel": "الرجاء اختيار نموذج للمتابعة",
+ "deleteHistoryConfirmation": "هل أنت متأكد أنك تريد حذف هذا السجل؟",
+ "editHistoryTitle": "أدخل عنواناً جديداً",
+ "temporaryChat": "محادثة مؤقتة",
+ "more": {
+ "copy": {
+ "group": "نسخ",
+ "asText": "نسخ كنص",
+ "asMarkdown": "نسخ كماركداون",
+ "success": "تم النسخ إلى الحافظة!"
+ },
+ "download": {
+ "group": "تنزيل",
+ "text": "ملف نصي (.txt)",
+ "markdown": "ماركداون (.md)",
+ "json": "ملف JSON (.json)"
+ },
+ "share": "مشاركة"
+ }
+}
\ No newline at end of file
diff --git a/src/assets/locale/ar/playground.json b/src/assets/locale/ar/playground.json
new file mode 100644
index 0000000..a5dc623
--- /dev/null
+++ b/src/assets/locale/ar/playground.json
@@ -0,0 +1,32 @@
+{
+ "ollamaState": {
+ "searching": "جارٍ البحث عن Ollama الخاص بك 🦙",
+ "running": "Ollama يعمل 🦙",
+ "notRunning": "تعذر الاتصال بـ Ollama 🦙",
+ "connectionError": "يبدو أنك تواجه خطأ في الاتصال. يرجى الرجوع إلى هذا
=u&&(this.b=n,r=this.f(),n=this.b);s--;)r[n]=r[n++-a];for(;8<=this.e;)this.e-=8,this.c--;this.b=n},C.prototype.Q=function(t,e){var r=this.a,n=this.b;this.A=t;for(var i,o,a,s,u=r.length;256!==(i=it(this,t));)if(256>i)n>=u&&(u=(r=this.f()).length),r[n++]=i;else for(s=Y[o=i-257],0u&&(u=(r=this.f()).length);s--;)r[n]=r[n++-a];for(;8<=this.e;)this.e-=8,this.c--;this.b=n},C.prototype.f=function(){var t,e,r=new(o?Uint8Array:Array)(this.b-32768),n=this.b-32768,i=this.a;if(o)r.set(i.subarray(32768,r.length));else for(t=0,e=r.length;tt;++t)i[t]=i[n+t];return this.b=32768,i},C.prototype.S=function(t){var e,r,n,i=this.input.length/this.c+1|0,a=this.input,s=this.a;return t&&("number"==typeof t.B&&(i=t.B),"number"==typeof t.M&&(i+=t.M)),r=2>i?(n=(a.length-this.c)/this.A[2]/2*258|0) e&&(this.a.length=e),t=this.a),this.buffer=t},ot.prototype.i=function(){for(var e=this.input.length;this.c >>0,p(n,r,r)!==y&&t(Error("invalid CRC-32 checksum: 0x"+p(n,r,r).toString(16)+" / 0x"+y.toString(16))),s.Z=u=(v[b++]|v[b++]<<8|v[b++]<<16|v[b++]<<24)>>>0,(4294967295&n.length)!==u&&t(Error("invalid input size: "+(4294967295&n.length)+" / "+u)),this.G.push(s),this.c=b}this.R=i;var w,m,E,x=this.G,A=0,k=0;for(w=0,m=x.length;w >>0!==at(e)&&t(Error("invalid adler-32 checksum")),e};var ut=8;function ct(t,e){this.input=t,this.a=new(o?Uint8Array:Array)(32768),this.k=ft.t;var r,n={};for(r in!e&&(e={})||"number"!=typeof e.compressionType||(this.k=e.compressionType),e)n[r]=e[r];n.outputBuffer=this.a,this.I=new w(this.input,n)}var ft=x;function ht(t,e){var r;return r=new ct(t).h(),e||(e={}),e.H?r:yt(r)}function lt(t,e){var r;return t.subarray=t.slice,r=new st(t).i(),e||(e={}),e.noBuffer?r:yt(r)}function pt(t,e){var r;return t.subarray=t.slice,r=new j(t).h(),e||(e={}),e.H?r:yt(r)}function dt(t,e){var r;return t.subarray=t.slice,r=new ot(t).i(),e||(e={}),e.H?r:yt(r)}function yt(t){var e,r,i=new n(t.length);for(e=0,r=t.length;e >24&255,u[c++]=s>>16&255,u[c++]=s>>8&255,u[c++]=255&s,u},e.deflate=function(t,e,r){process.nextTick((function(){var n,i;try{i=ht(t,r)}catch(t){n=t}e(n,i)}))},e.deflateSync=ht,e.inflate=function(t,e,r){process.nextTick((function(){var n,i;try{i=lt(t,r)}catch(t){n=t}e(n,i)}))},e.inflateSync=lt,e.gzip=function(t,e,r){process.nextTick((function(){var n,i;try{i=pt(t,r)}catch(t){n=t}e(n,i)}))},e.gzipSync=pt,e.gunzip=function(t,e,r){process.nextTick((function(){var n,i;try{i=dt(t,r)}catch(t){n=t}e(n,i)}))},e.gunzipSync=dt}).call(this)},711:t=>{t.exports={OSD_ONLY:"0",AUTO_OSD:"1",AUTO_ONLY:"2",AUTO:"3",SINGLE_COLUMN:"4",SINGLE_BLOCK_VERT_TEXT:"5",SINGLE_BLOCK:"6",SINGLE_LINE:"7",SINGLE_WORD:"8",CIRCLE_WORD:"9",SINGLE_CHAR:"10",SPARSE_TEXT:"11",SPARSE_TEXT_OSD:"12",RAW_LINE:"13"}},862:t=>{t.exports={COLOR:0,GREY:1,BINARY:2}},129:(t,e,r)=>{function n(t){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},n(t)}var i=r(670);t.exports=function(t){var e={};return"undefined"!=typeof WorkerGlobalScope?e.type="webworker":i()?e.type="electron":"object"===("undefined"==typeof document?"undefined":n(document))?e.type="browser":"object"===("undefined"==typeof process?"undefined":n(process))&&(e.type="node"),void 0===t?e:e[t]}},185:function(t,e){var r=this,n=!1;e.logging=n,e.setLogging=function(t){n=t},e.log=function(){for(var t=arguments.length,e=new Array(t),i=0;i {var n=r(160),i=n.set,o=n.get,a=n.del;t.exports={readCache:o,writeCache:i,deleteCache:a,checkCache:function(t){return o(t).then((function(t){return void 0!==t}))}}},485:(t,e,r)=>{function n(){"use strict";n=function(){return t};var t={},e=Object.prototype,r=e.hasOwnProperty,o=Object.defineProperty||function(t,e,r){t[e]=r.value},a="function"==typeof Symbol?Symbol:{},s=a.iterator||"@@iterator",u=a.asyncIterator||"@@asyncIterator",c=a.toStringTag||"@@toStringTag";function f(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{f({},"")}catch(t){f=function(t,e,r){return t[e]=r}}function h(t,e,r,n){var i=e&&e.prototype instanceof d?e:d,a=Object.create(i.prototype),s=new I(n||[]);return o(a,"_invoke",{value:A(t,r,s)}),a}function l(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}t.wrap=h;var p={};function d(){}function y(){}function g(){}var v={};f(v,s,(function(){return this}));var b=Object.getPrototypeOf,w=b&&b(b(S([])));w&&w!==e&&r.call(w,s)&&(v=w);var m=g.prototype=d.prototype=Object.create(v);function E(t){["next","throw","return"].forEach((function(e){f(t,e,(function(t){return this._invoke(e,t)}))}))}function x(t,e){function n(o,a,s,u){var c=l(t[o],t,a);if("throw"!==c.type){var f=c.arg,h=f.value;return h&&"object"==i(h)&&r.call(h,"__await")?e.resolve(h.__await).then((function(t){n("next",t,s,u)}),(function(t){n("throw",t,s,u)})):e.resolve(h).then((function(t){f.value=t,s(f)}),(function(t){return n("throw",t,s,u)}))}u(c.arg)}var a;o(this,"_invoke",{value:function(t,r){function i(){return new e((function(e,i){n(t,r,e,i)}))}return a=a?a.then(i,i):i()}})}function A(t,e,r){var n="suspendedStart";return function(i,o){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===i)throw o;return{value:void 0,done:!0}}for(r.method=i,r.arg=o;;){var a=r.delegate;if(a){var s=k(a,r);if(s){if(s===p)continue;return s}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var u=l(t,e,r);if("normal"===u.type){if(n=r.done?"completed":"suspendedYield",u.arg===p)continue;return{value:u.arg,done:r.done}}"throw"===u.type&&(n="completed",r.method="throw",r.arg=u.arg)}}}function k(t,e){var r=e.method,n=t.iterator[r];if(void 0===n)return e.delegate=null,"throw"===r&&t.iterator.return&&(e.method="return",e.arg=void 0,k(t,e),"throw"===e.method)||"return"!==r&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+r+"' method")),p;var i=l(n,t.iterator,e.arg);if("throw"===i.type)return e.method="throw",e.arg=i.arg,e.delegate=null,p;var o=i.arg;return o?o.done?(e[t.resultName]=o.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=void 0),e.delegate=null,p):o:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,p)}function _(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function O(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function I(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(_,this),this.reset(!0)}function S(t){if(t){var e=t[s];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,i=function e(){for(;++n =0;--i){var o=this.tryEntries[i],a=o.completion;if("root"===o.tryLoc)return n("end");if(o.tryLoc<=this.prev){var s=r.call(o,"catchLoc"),u=r.call(o,"finallyLoc");if(s&&u){if(this.prev =0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev =0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),O(r),p}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var i=n.arg;O(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:S(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),p}},t}function i(t){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i(t)}function o(t,e,r,n,i,o,a){try{var s=t[o](a),u=s.value}catch(t){return void r(t)}s.done?e(u):Promise.resolve(u).then(n,i)}var a=r(506).simd,s=r(147).HO._X;t.exports=function(){var t,e=(t=n().mark((function t(e,o,u){var c,f,h,l;return n().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(void 0!==r.g.TesseractCore){t.next=20;break}if(c="loading tesseract core",u.progress({status:c,progress:0}),"js"!==(f=o||"https://cdn.jsdelivr.net/npm/tesseract.js-core@v".concat(s.substring(1))).slice(-2)){t.next=8;break}h=f,t.next=12;break;case 8:return t.next=10,a();case 10:l=t.sent,h="".concat(f.replace(/\/$/,""),l?e?"/tesseract-core-simd-lstm.wasm.js":"/tesseract-core-simd.wasm.js":e?"/tesseract-core-lstm.wasm.js":"/tesseract-core.wasm.js");case 12:if(r.g.importScripts(h),void 0!==r.g.TesseractCore||void 0===r.g.TesseractCoreWASM||"object"!==("undefined"==typeof WebAssembly?"undefined":i(WebAssembly))){t.next=17;break}r.g.TesseractCore=r.g.TesseractCoreWASM,t.next=19;break;case 17:if(void 0!==r.g.TesseractCore){t.next=19;break}throw Error("Failed to load TesseractCore");case 19:u.progress({status:c,progress:1});case 20:return t.abrupt("return",r.g.TesseractCore);case 21:case"end":return t.stop()}}),t)})),function(){var e=this,r=arguments;return new Promise((function(n,i){var a=t.apply(e,r);function s(t){o(a,n,i,s,u,"next",t)}function u(t){o(a,n,i,s,u,"throw",t)}s(void 0)}))});return function(t,r,n){return e.apply(this,arguments)}}()},687:(t,e,r)=>{t.exports=r(645).gunzipSync},730:t=>{t.exports={text:!0,blocks:!0,layoutBlocks:!1,hocr:!0,tsv:!0,box:!1,unlv:!1,osd:!1,pdf:!1,imageColor:!1,imageGrey:!1,imageBinary:!1,debug:!1}},688:(t,e,r)=>{var n=r(711);t.exports={tessedit_pageseg_mode:n.SINGLE_BLOCK,tessedit_char_whitelist:"",tessjs_create_hocr:"1",tessjs_create_tsv:"1",tessjs_create_box:"0",tessjs_create_unlv:"0",tessjs_create_osd:"0"}},217:function(t,e,r){var n=this;function i(t){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i(t)}function o(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function a(t){for(var e=1;e t.length)&&(e=t.length);for(var r=0,n=new Array(e);r =0;--i){var o=this.tryEntries[i],a=o.completion;if("root"===o.tryLoc)return n("end");if(o.tryLoc<=this.prev){var s=r.call(o,"catchLoc"),u=r.call(o,"finallyLoc");if(s&&u){if(this.prev =0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev =0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),O(r),p}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var i=n.arg;O(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:S(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),p}},t}function f(t,e,r,n,i,o,a){try{var s=t[o](a),u=s.value}catch(t){return void r(t)}s.done?e(u):Promise.resolve(u).then(n,i)}function h(t){return function(){var e=this,r=arguments;return new Promise((function(n,i){var o=t.apply(e,r);function a(t){f(o,n,i,a,s,"next",t)}function s(t){f(o,n,i,a,s,"throw",t)}a(void 0)}))}}r(760);var l,p,d,y,g=r(975),v=r(333),b=r(129)("type"),w=r(472),m=r(688),E=r(730),x=r(185),A=x.log,k=x.setLogging,_=r(711),O=null,I={},S=m,L=!1,U=function(){var t=h(c().mark((function t(e,r){var n,i,o,a,s,u,f,h;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(n=e.workerId,i=e.jobId,o=e.payload.options,a=o.lstmOnly,s=o.corePath,u=o.logging,k(u),f="initializing tesseract",l){t.next=11;break}return t.next=6,I.getCore(a,s,r);case 6:h=t.sent,r.progress({workerId:n,status:f,progress:0}),h({TesseractProgress:function(t){p.progress({workerId:n,jobId:i,status:"recognizing text",progress:Math.max(0,(t-30)/70)})}}).then((function(t){l=t,r.progress({workerId:n,status:f,progress:1}),r.resolve({loaded:!0})})),t.next=12;break;case 11:r.resolve({loaded:!0});case 12:case"end":return t.stop()}}),t)})));return function(e,r){return t.apply(this,arguments)}}(),j=function(){var t=h(c().mark((function t(e,r){var n,i,o,a,s;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:i=e.workerId,o=e.payload,a=o.method,s=o.args,A("[".concat(i,"]: FS.").concat(a)),r.resolve((n=l.FS)[a].apply(n,function(t){if(Array.isArray(t))return u(t)}(c=s)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(c)||function(t,e){if(t){if("string"==typeof t)return u(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?u(t,e):void 0}}(c)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()));case 3:case"end":return t.stop()}var c}),t)})));return function(e,r){return t.apply(this,arguments)}}(),B=function(){var t=h(c().mark((function t(e,r){var n,i,o,a,s,u,f,p,v,w,m,E,x,k,_;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return n=e.workerId,i=e.payload,o=i.langs,a=i.options,s=a.langPath,u=a.dataPath,f=a.cachePath,p=a.cacheMethod,v=a.gzip,w=void 0===v||v,m=a.lstmOnly,d=o,y={langPath:s,dataPath:u,cachePath:f,cacheMethod:p,gzip:w,lstmOnly:m},E="loading language traineddata",x="string"==typeof o?o.split("+"):o,k=0,_=function(){var t=h(c().mark((function t(e){var i,o,a,h,d,y,v,_,O;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return i="string"==typeof e?e:e.code,o=["refresh","none"].includes(p)?function(){return Promise.resolve()}:I.readCache,a=null,h=!1,t.prev=4,t.next=7,o("".concat(f||".","/").concat(i,".traineddata"));case 7:if(void 0===(d=t.sent)){t.next=14;break}A("[".concat(n,"]: Load ").concat(i,".traineddata from cache")),a=d,L=!0,t.next=15;break;case 14:throw Error("Not found in cache");case 15:t.next=45;break;case 17:if(t.prev=17,t.t0=t.catch(4),h=!0,A("[".concat(n,"]: Load ").concat(i,".traineddata from ").concat(s)),"string"!=typeof e){t.next=44;break}if(y=null,v=s||"https://cdn.jsdelivr.net/npm/@tesseract.js-data/".concat(i,m?"/4.0.0_best_int":"/4.0.0"),("node"!==b||g(v)||v.startsWith("moz-extension://")||v.startsWith("chrome-extension://")||v.startsWith("file://"))&&(y=v.replace(/\/$/,"")),null===y){t.next=39;break}return _="".concat(y,"/").concat(i,".traineddata").concat(w?".gz":""),t.next=29,("webworker"===b?fetch:I.fetch)(_);case 29:if((O=t.sent).ok){t.next=32;break}throw Error("Network error while fetching ".concat(_,". Response code: ").concat(O.status));case 32:return t.t1=Uint8Array,t.next=35,O.arrayBuffer();case 35:t.t2=t.sent,a=new t.t1(t.t2),t.next=42;break;case 39:return t.next=41,I.readCache("".concat(v,"/").concat(i,".traineddata").concat(w?".gz":""));case 41:a=t.sent;case 42:t.next=45;break;case 44:a=e.data;case 45:if(k+=.5/x.length,r&&r.progress({workerId:n,status:E,progress:k}),(31===a[0]&&139===a[1]||31===a[1]&&139===a[0])&&(a=I.gunzip(a)),l){if(u)try{l.FS.mkdir(u)}catch(t){r&&r.reject(t.toString())}l.FS.writeFile("".concat(u||".","/").concat(i,".traineddata"),a)}if(!h||!["write","refresh",void 0].includes(p)){t.next=60;break}return t.prev=51,t.next=54,I.writeCache("".concat(f||".","/").concat(i,".traineddata"),a);case 54:t.next=60;break;case 56:t.prev=56,t.t3=t.catch(51),A("[".concat(n,"]: Failed to write ").concat(i,".traineddata to cache due to error:")),A(t.t3.toString());case 60:k+=.5/x.length,100===Math.round(100*k)&&(k=1),r&&r.progress({workerId:n,status:E,progress:k});case 63:case"end":return t.stop()}}),t,null,[[4,17],[51,56]])})));return function(e){return t.apply(this,arguments)}}(),r&&r.progress({workerId:n,status:E,progress:0}),t.prev=8,t.next=11,Promise.all(x.map(_));case 11:r&&r.resolve(o),t.next=17;break;case 14:t.prev=14,t.t0=t.catch(8),r&&r.reject(t.t0.toString());case 17:case"end":return t.stop()}}),t,null,[[8,14]])})));return function(e,r){return t.apply(this,arguments)}}(),T=function(){var t=h(c().mark((function t(e,r){var n,i,o;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:n=e.payload.params,i=["ambigs_debug_level","user_words_suffix","user_patterns_suffix","user_patterns_suffix","load_system_dawg","load_freq_dawg","load_unambig_dawg","load_punc_dawg","load_number_dawg","load_bigram_dawg","tessedit_ocr_engine_mode","tessedit_init_config_only","language_model_ngram_on","language_model_use_sigmoidal_certainty"],(o=Object.keys(n).filter((function(t){return i.includes(t)})).join(", ")).length>0&&console.log("Attempted to set parameters that can only be set during initialization: ".concat(o)),Object.keys(n).filter((function(t){return!t.startsWith("tessjs_")})).forEach((function(t){O.SetVariable(t,n[t])})),S=a(a({},S),n),void 0!==r&&r.resolve(S);case 7:case"end":return t.stop()}}),t)})));return function(e,r){return t.apply(this,arguments)}}(),P=function(){var t=h(c().mark((function t(e,r){var n,o,a,s,u,f,h,p,g,v,b,w,E,x;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(n=e.workerId,o=e.payload,a=o.langs,s=o.oem,u=o.config,f="string"==typeof a?a:a.map((function(t){return"string"==typeof t?t:t.data})).join("+"),h="initializing api",t.prev=3,r.progress({workerId:n,status:h,progress:0}),null!==O&&O.End(),u&&"object"===i(u)&&Object.keys(u).length>0?g=JSON.stringify(u).replace(/,/g,"\n").replace(/:/g," ").replace(/["'{}]/g,""):u&&"string"==typeof u&&(g=u),"string"==typeof g&&(p="/config",l.FS.writeFile(p,g)),O=new l.TessBaseAPI,-1!==(v=O.Init(null,f,s,p))){t.next=30;break}if(!["write","refresh",void 0].includes(y.cacheMethod)){t.next=30;break}return b=f.split("+"),w=b.map((function(t){return I.deleteCache("".concat(y.cachePath||".","/").concat(t,".traineddata"))})),t.next=16,Promise.all(w);case 16:if(E=l.FS.readFile("/debugDev.txt",{encoding:"utf8",flags:"a+"}),!L||!/components are not present/.test(E)){t.next=30;break}return A("Data from cache missing requested OEM model. Attempting to refresh cache with new language data."),t.next=21,B({workerId:n,payload:{langs:d,options:y}});case 21:if(-1!==(v=O.Init(null,f,s,p))){t.next=29;break}return A("Language data refresh failed."),x=b.map((function(t){return I.deleteCache("".concat(y.cachePath||".","/").concat(t,".traineddata"))})),t.next=27,Promise.all(x);case 27:t.next=30;break;case 29:A("Language data refresh successful.");case 30:return-1===v&&r.reject("initialization failed"),S=m,t.next=34,T({payload:{params:S}});case 34:r.progress({workerId:n,status:h,progress:1}),r.resolve(),t.next=41;break;case 38:t.prev=38,t.t0=t.catch(3),r.reject(t.t0.toString());case 41:case"end":return t.stop()}}),t,null,[[3,38]])})));return function(e,r){return t.apply(this,arguments)}}(),R=function(t,e){var r=new l.TessPDFRenderer("tesseract-ocr","/",e);return r.BeginDocument(t),r.AddImage(O),r.EndDocument(),l._free(r),l.FS.readFile("/tesseract-ocr.pdf")},C=function(){var t=h(c().mark((function t(e,r){var n,i,o;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:n=e.payload,i=n.title,o=n.textonly,r.resolve(R(i,o));case 2:case"end":return t.stop()}}),t)})));return function(e,r){return t.apply(this,arguments)}}(),N=function(t){var e=JSON.parse(JSON.stringify(E));"1"===S.tessjs_create_box&&(e.box=!0),"1"===S.tessjs_create_hocr&&(e.hocr=!0),"1"===S.tessjs_create_osd&&(e.osd=!0),"1"===S.tessjs_create_tsv&&(e.tsv=!0),"1"===S.tessjs_create_unlv&&(e.unlv=!0);for(var r=["imageColor","imageGrey","imageBinary","layoutBlocks","debug"],n=0,i=0,o=Object.keys(t);i 0)for(f=0,h=Object.keys(a);f 0)for(O.SaveParameters(),d=0,y=Object.keys(u);d =.005?w(l,O,o,x=S):(I&&w(l,O,o),x=0)):(x=a.rotateRadians||0,w(l,O,o,x)),"object"===i(L=a.rectangle)&&O.SetRectangle(L.left,L.top,L.width,L.height),E?(s.layoutBlocks&&O.AnalyseLayout(),A("Skipping recognition: all output options requiring recognition are disabled.")):O.Recognize(null),U=a.pdfTitle,j=a.pdfTextOnly,(B=v(l,O,m,{pdfTitle:U,pdfTextOnly:j,skipRecognition:E})).rotateRadians=x,s.debug&&l.FS.unlink("/debugInternal.txt"),Object.keys(u).length>0&&O.RestoreParameters(),r.resolve(B)}catch(t){r.reject(t.toString())}case 2:case"end":return t.stop()}}),t)})));return function(e,r){return t.apply(this,arguments)}}(),G=function(){var t=h(c().mark((function t(e,r){var n,i,o,a,s;return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:n=e.payload.image;try{w(l,O,n),i=new l.OSResults,O.DetectOS(i)?(o=i.best_result,a=o.orientation_id,s=o.script_id,r.resolve({tesseract_script_id:s,script:i.unicharset.get_script_from_script_id(s),script_confidence:o.sconfidence,orientation_degrees:[0,270,180,90][a],orientation_confidence:o.oconfidence})):r.resolve({tesseract_script_id:null,script:null,script_confidence:null,orientation_degrees:null,orientation_confidence:null})}catch(t){r.reject(t.toString())}case 2:case"end":return t.stop()}}),t)})));return function(e,r){return t.apply(this,arguments)}}(),z=function(){var t=h(c().mark((function t(e,r){return c().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:try{null!==O&&O.End(),r.resolve({terminated:!0})}catch(t){r.reject(t.toString())}case 1:case"end":return t.stop()}}),t)})));return function(e,r){return t.apply(this,arguments)}}();e.dispatchHandlers=function(t,e){var r=function(r,n){var i={jobId:t.jobId,workerId:t.workerId,action:t.action};e(a(a({},i),{},{status:r,data:n}))};r.resolve=r.bind(n,"resolve"),r.reject=r.bind(n,"reject"),r.progress=r.bind(n,"progress"),p=r,{load:U,FS:j,loadLanguage:B,initialize:P,setParameters:T,recognize:F,getPDF:C,detect:G,terminate:z}[t.action](t,r).catch((function(t){return r.reject(t.toString())}))},e.setAdapter=function(t){I=t}},629:t=>{t.exports=function(t){for(var e,r="",n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",i=new Uint8Array(t),o=i.byteLength,a=o%3,s=o-a,u=0;u >18]+n[(258048&e)>>12]+n[(4032&e)>>6]+n[63&e];return 1===a?(e=i[s],r+="".concat(n[(252&e)>>2]+n[(3&e)<<4],"==")):2===a&&(e=i[s]<<8|i[s+1],r+="".concat(n[(64512&e)>>10]+n[(1008&e)>>4]+n[(15&e)<<2],"=")),r}},333:(t,e,r)=>{var n=r(629),i=r(862),o=function(t){var e=t.split("\n");if(" "===e[0].substring(0,2))for(var r=0;r0){var S=O.get_n(),L=O.get_x(),U=O.get_y();I=[];for(var j=0;j {var n=r(33).lW;function i(t){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i(t)}function o(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function a(t){for(var e=1;e3&&void 0!==arguments[3]?arguments[3]:0,s=66===r[0]&&77===r[1]||66===r[1]&&77===r[0],c=parseInt(null===(i=r.slice(0,500).join(" ").match(/1 18 0 3 0 0 0 1 0 (\d)/))||void 0===i?void 0:i[1],10)||1;if(s){var f=n.from(Array.from(a(a({},r),{},{length:Object.keys(r).length}))),h=u.decode(f);t.FS.writeFile("/input",u.encode(h).data)}else t.FS.writeFile("/input",r);if(1===e.SetImageFile(c,o))throw Error("Error attempting to read image.")}},160:(t,e,r)=>{"use strict";function n(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r 1&&void 0!==arguments[1]?arguments[1]:s())("readonly",(function(e){return i(e.get(t))}))}function c(t,e){return(arguments.length>2&&void 0!==arguments[2]?arguments[2]:s())("readwrite",(function(r){return r.put(e,t),i(r.transaction)}))}function f(t){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:s())("readwrite",(function(e){return t.forEach((function(t){return e.put(t[1],t[0])})),i(e.transaction)}))}function h(t){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:s())("readonly",(function(e){return Promise.all(t.map((function(t){return i(e.get(t))})))}))}function l(t,e){return(arguments.length>2&&void 0!==arguments[2]?arguments[2]:s())("readwrite",(function(r){return new Promise((function(n,o){r.get(t).onsuccess=function(){try{r.put(e(this.result),t),n(i(r.transaction))}catch(t){o(t)}}}))}))}function p(t){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:s())("readwrite",(function(e){return e.delete(t),i(e.transaction)}))}function d(t){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:s())("readwrite",(function(e){return t.forEach((function(t){return e.delete(t)})),i(e.transaction)}))}function y(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:s())("readwrite",(function(t){return t.clear(),i(t.transaction)}))}function g(t,e){return t.openCursor().onsuccess=function(){this.result&&(e(this.result),this.result.continue())},i(t.transaction)}function v(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:s())("readonly",(function(t){if(t.getAllKeys)return i(t.getAllKeys());var e=[];return g(t,(function(t){return e.push(t.key)})).then((function(){return e}))}))}function b(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:s())("readonly",(function(t){if(t.getAll)return i(t.getAll());var e=[];return g(t,(function(t){return e.push(t.value)})).then((function(){return e}))}))}function w(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:s();return t("readonly",(function(e){if(e.getAll&&e.getAllKeys)return Promise.all([i(e.getAllKeys()),i(e.getAll())]).then((function(t){var e,r,i=(r=2,function(t){if(Array.isArray(t))return t}(e=t)||function(t,e){var r=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=r){var n,i,o,a,s=[],u=!0,c=!1;try{if(o=(r=r.call(t)).next,0===e){if(Object(r)!==r)return;u=!1}else for(;!(u=(n=o.call(r)).done)&&(s.push(n.value),s.length!==e);u=!0);}catch(t){c=!0,i=t}finally{try{if(!u&&null!=r.return&&(a=r.return(),Object(a)!==a))return}finally{if(c)throw i}}return s}}(e,r)||function(t,e){if(t){if("string"==typeof t)return n(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(t,e):void 0}}(e,r)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),o=i[0],a=i[1];return o.map((function(t,e){return[t,a[e]]}))}));var r=[];return t("readonly",(function(t){return g(t,(function(t){return r.push([t.key,t.value])})).then((function(){return r}))}))}))}r.r(e),r.d(e,{clear:()=>y,createStore:()=>o,del:()=>p,delMany:()=>d,entries:()=>w,get:()=>u,getMany:()=>h,keys:()=>v,promisifyRequest:()=>i,set:()=>c,setMany:()=>f,update:()=>l,values:()=>b})},147:t=>{"use strict";t.exports=JSON.parse('{"HO":{"_X":"^5.1.1"}}')}},e={};function r(n){var i=e[n];if(void 0!==i)return i.exports;var o=e[n]={id:n,loaded:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.loaded=!0,o.exports}r.d=(t,e)=>{for(var n in e)r.o(e,n)&&!r.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.nmd=t=>(t.paths=[],t.children||(t.children=[]),t),(()=>{function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}function n(e,r,n){return(r=function(e){var r=function(e,r){if("object"!==t(e)||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var i=n.call(e,"string");if("object"!==t(i))return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"===t(r)?r:String(r)}(r))in e?Object.defineProperty(e,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[r]=n,e}var i=r(217),o=r(485),a=r(687),s=r(779);r.g.addEventListener("message",(function(t){var e=t.data;i.dispatchHandlers(e,(function(t){return postMessage(t)}))})),i.setAdapter(function(t){for(var r=1;r { + const { mode } = useDarkMode() + + return ( + ++ ) +} + +export const SidepanelRouting = () => { + const { mode } = useDarkMode() + + return ( +}> + + + ++ ) +} diff --git a/src/routes/index.tsx b/src/routes/firefox-route.tsx similarity index 59% rename from src/routes/index.tsx rename to src/routes/firefox-route.tsx index 2a4dd60..a338bf5 100644 --- a/src/routes/index.tsx +++ b/src/routes/firefox-route.tsx @@ -1,22 +1,15 @@ import { Suspense } from "react" -import { useTranslation } from "react-i18next" import { useDarkMode } from "~/hooks/useDarkmode" -import { OptionRoutingChrome, SidepanelRoutingChrome } from "./chrome" import { OptionRoutingFirefox, SidepanelRoutingFirefox } from "./firefox" import { PageAssistLoader } from "@/components/Common/PageAssistLoader" export const OptionRouting = () => { const { mode } = useDarkMode() - const { i18n } = useTranslation() return (}> + + + ) @@ -24,16 +17,11 @@ export const OptionRouting = () => { export const SidepanelRouting = () => { const { mode } = useDarkMode() - const { i18n } = useTranslation() return (}> - {import.meta.env.BROWSER === "chrome" ? ( - - ) : ( - - )} + ) diff --git a/src/services/elevenlabs.ts b/src/services/elevenlabs.ts new file mode 100644 index 0000000..d87cdef --- /dev/null +++ b/src/services/elevenlabs.ts @@ -0,0 +1,49 @@ +import axios from 'axios'; +export interface Voice { + voice_id: string; + name: string; +} + +export interface Model { + model_id: string; + name: string; +} + +const BASE_URL = 'https://api.elevenlabs.io/v1'; + +export const getVoices = async (apiKey: string): Promise}> - {import.meta.env.BROWSER === "chrome" ? ( - - ) : ( - - )} + => { + const response = await axios.get(`${BASE_URL}/voices`, { + headers: { 'xi-api-key': apiKey } + }); + return response.data.voices; +}; + +export const getModels = async (apiKey: string): Promise => { + const response = await axios.get(`${BASE_URL}/models`, { + headers: { 'xi-api-key': apiKey } + }); + return response.data; +}; + +export const generateSpeech = async ( + apiKey: string, + text: string, + voiceId: string, + modelId: string +): Promise => { + const response = await axios.post( + `${BASE_URL}/text-to-speech/${voiceId}`, + { + text, + model_id: modelId, + }, + { + headers: { + 'xi-api-key': apiKey, + 'Content-Type': 'application/json', + }, + responseType: 'arraybuffer', + } + ); + return response.data; +}; \ No newline at end of file diff --git a/src/services/model-settings.ts b/src/services/model-settings.ts index 1993442..730d619 100644 --- a/src/services/model-settings.ts +++ b/src/services/model-settings.ts @@ -32,6 +32,7 @@ type ModelSettings = { useMLock?: boolean useMMap?: boolean vocabOnly?: boolean + minP?: number } const keys = [ @@ -62,7 +63,8 @@ const keys = [ "typicalP", "useMLock", "useMMap", - "vocabOnly" + "vocabOnly", + "minP", ] export const getAllModelSettings = async () => { diff --git a/src/services/ollama.ts b/src/services/ollama.ts index 42046ff..e19d704 100644 --- a/src/services/ollama.ts +++ b/src/services/ollama.ts @@ -231,8 +231,8 @@ export const setOllamaURL = async (ollamaURL: string) => { "http://127.0.0.1:" ) } - await urlRewriteRuntime(cleanUrl(formattedUrl)) await storage.set("ollamaURL", cleanUrl(formattedUrl)) + await urlRewriteRuntime(cleanUrl(formattedUrl)) } export const systemPromptForNonRag = async () => { diff --git a/src/services/tts.ts b/src/services/tts.ts index 847efb4..ace7b05 100644 --- a/src/services/tts.ts +++ b/src/services/tts.ts @@ -4,7 +4,7 @@ const storage = new Storage() const DEFAULT_TTS_PROVIDER = "browser" -const AVAILABLE_TTS_PROVIDERS = ["browser"] as const +const AVAILABLE_TTS_PROVIDERS = ["browser", "elevenlabs"] as const export const getTTSProvider = async (): Promise< (typeof AVAILABLE_TTS_PROVIDERS)[number] @@ -63,22 +63,78 @@ export const setSSMLEnabled = async (isSSMLEnabled: boolean) => { await storage.set("isSSMLEnabled", isSSMLEnabled.toString()) } +export const getElevenLabsApiKey = async () => { + const data = await storage.get("elevenLabsApiKey") + return data +} + +export const setElevenLabsApiKey = async (elevenLabsApiKey: string) => { + await storage.set("elevenLabsApiKey", elevenLabsApiKey) +} + +export const getElevenLabsVoiceId = async () => { + const data = await storage.get("elevenLabsVoiceId") + return data +} + +export const setElevenLabsVoiceId = async (elevenLabsVoiceId: string) => { + await storage.set("elevenLabsVoiceId", elevenLabsVoiceId) +} + +export const getElevenLabsModel = async () => { + const data = await storage.get("elevenLabsModel") + return data +} + +export const setElevenLabsModel = async (elevenLabsModel: string) => { + await storage.set("elevenLabsModel", elevenLabsModel) +} + +export const getResponseSplitting = async () => { + const data = await storage.get("ttsResponseSplitting") + if (!data || data.length === 0 || data === "") { + return "punctuation" + } + return data +} + +export const setResponseSplitting = async (responseSplitting: string) => { + await storage.set("ttsResponseSplitting", responseSplitting) +} + export const getTTSSettings = async () => { - const [ttsEnabled, ttsProvider, browserTTSVoices, voice, ssmlEnabled] = - await Promise.all([ - isTTSEnabled(), - getTTSProvider(), - getBrowserTTSVoices(), - getVoice(), - isSSMLEnabled() - ]) + const [ + ttsEnabled, + ttsProvider, + browserTTSVoices, + voice, + ssmlEnabled, + elevenLabsApiKey, + elevenLabsVoiceId, + elevenLabsModel, + responseSplitting + ] = await Promise.all([ + isTTSEnabled(), + getTTSProvider(), + getBrowserTTSVoices(), + getVoice(), + isSSMLEnabled(), + getElevenLabsApiKey(), + getElevenLabsVoiceId(), + getElevenLabsModel(), + getResponseSplitting() + ]) return { ttsEnabled, ttsProvider, browserTTSVoices, voice, - ssmlEnabled + ssmlEnabled, + elevenLabsApiKey, + elevenLabsVoiceId, + elevenLabsModel, + responseSplitting } } @@ -86,17 +142,29 @@ export const setTTSSettings = async ({ ttsEnabled, ttsProvider, voice, - ssmlEnabled + ssmlEnabled, + elevenLabsApiKey, + elevenLabsVoiceId, + elevenLabsModel, + responseSplitting }: { ttsEnabled: boolean ttsProvider: string voice: string ssmlEnabled: boolean + elevenLabsApiKey: string + elevenLabsVoiceId: string + elevenLabsModel: string + responseSplitting: string }) => { await Promise.all([ setTTSEnabled(ttsEnabled), setTTSProvider(ttsProvider), setVoice(voice), - setSSMLEnabled(ssmlEnabled) + setSSMLEnabled(ssmlEnabled), + setElevenLabsApiKey(elevenLabsApiKey), + setElevenLabsVoiceId(elevenLabsVoiceId), + setElevenLabsModel(elevenLabsModel), + setResponseSplitting(responseSplitting) ]) } diff --git a/src/store/index.tsx b/src/store/index.tsx index 9bdee7b..49b2072 100644 --- a/src/store/index.tsx +++ b/src/store/index.tsx @@ -45,6 +45,9 @@ type State = { selectedQuickPrompt: string | null setSelectedQuickPrompt: (selectedQuickPrompt: string) => void + + useOCR: boolean + setUseOCR: (useOCR: boolean) => void } export const useStoreMessage = create ((set) => ({ @@ -79,5 +82,8 @@ export const useStoreMessage = create ((set) => ({ setSelectedSystemPrompt: (selectedSystemPrompt) => set({ selectedSystemPrompt }), selectedQuickPrompt: null, - setSelectedQuickPrompt: (selectedQuickPrompt) => set({ selectedQuickPrompt }) + setSelectedQuickPrompt: (selectedQuickPrompt) => set({ selectedQuickPrompt }), + + useOCR: false, + setUseOCR: (useOCR) => set({ useOCR }) })) diff --git a/src/store/model.tsx b/src/store/model.tsx index 5514b24..9ef8546 100644 --- a/src/store/model.tsx +++ b/src/store/model.tsx @@ -30,6 +30,7 @@ type CurrentChatModelSettings = { useMMap?: boolean vocabOnly?: boolean seed?: number + minP?: number setF16KV?: (f16KV: boolean) => void setFrequencyPenalty?: (frequencyPenalty: number) => void @@ -65,6 +66,8 @@ type CurrentChatModelSettings = { reset: () => void systemPrompt?: string setSystemPrompt: (systemPrompt: string) => void + + setMinP: (minP: number) => void } export const useStoreChatModelSettings = create ( @@ -103,6 +106,7 @@ export const useStoreChatModelSettings = create ( seetSeed: (seed: number) => set({ seed }), setX: (key: string, value: any) => set({ [key]: value }), systemPrompt: undefined, + setMinP: (minP: number) => set({ minP }), setSystemPrompt: (systemPrompt: string) => set({ systemPrompt }), reset: () => set({ @@ -135,7 +139,8 @@ export const useStoreChatModelSettings = create ( useMMap: undefined, vocabOnly: undefined, seed: undefined, - systemPrompt: undefined + systemPrompt: undefined, + minP: undefined, }) }) ) diff --git a/src/store/option.tsx b/src/store/option.tsx index 49911cd..f1d27ea 100644 --- a/src/store/option.tsx +++ b/src/store/option.tsx @@ -68,6 +68,9 @@ type State = { temporaryChat: boolean setTemporaryChat: (temporaryChat: boolean) => void + + useOCR: boolean + setUseOCR: (useOCR: boolean) => void } export const useStoreMessageOption = create ((set) => ({ @@ -109,4 +112,7 @@ export const useStoreMessageOption = create ((set) => ({ temporaryChat: false, setTemporaryChat: (temporaryChat) => set({ temporaryChat }), + + useOCR: false, + setUseOCR: (useOCR) => set({ useOCR }), })) diff --git a/src/utils/human-message.tsx b/src/utils/human-message.tsx index 9c1200f..073a66e 100644 --- a/src/utils/human-message.tsx +++ b/src/utils/human-message.tsx @@ -4,14 +4,33 @@ import { HumanMessage, type MessageContent } from "@langchain/core/messages" type HumanMessageType = { content: MessageContent model: string + useOCR: boolean } -export const humanMessageFormatter = ({ content, model }: HumanMessageType) => { +export const humanMessageFormatter = async ({ + content, + model, + useOCR = false +}: HumanMessageType) => { const isCustom = isCustomModel(model) if (isCustom) { if (typeof content !== "string") { if (content.length > 1) { + if (useOCR) { + //@ts-ignore + const imageUrl = content[1].image_url + const ocrText = await processImageForOCR(imageUrl) + //@ts-ignore + const ocrPROMPT = `${content[0].text} + +[IMAGE OCR TEXT] +${ocrText}` + return new HumanMessage({ + content: ocrPROMPT + }) + } + // this means that we need to reformat the image_url const newContent: MessageContent = [ { @@ -40,6 +59,21 @@ export const humanMessageFormatter = ({ content, model }: HumanMessageType) => { } } + if (useOCR) { + if (typeof content !== "string" && content.length > 1) { + //@ts-ignore + const ocrText = await processImageForOCR(content[1].image_url) + //@ts-ignore + const ocrPROMPT = `${content[0].text} + +[IMAGE OCR TEXT] +${ocrText}` + return new HumanMessage({ + content: ocrPROMPT + }) + } + } + return new HumanMessage({ content }) diff --git a/src/utils/ocr.ts b/src/utils/ocr.ts new file mode 100644 index 0000000..e56a9ab --- /dev/null +++ b/src/utils/ocr.ts @@ -0,0 +1,17 @@ +import { createWorker } from 'tesseract.js'; + +export async function processImageForOCR(imageData: string): Promise { + const worker = await createWorker('eng-fast', undefined, { + workerPath: "/ocr/worker.min.js", + workerBlobURL: false, + corePath: "/ocr/tesseract-core-simd.js", + errorHandler: e => console.error(e), + langPath: "/ocr/lang" + }); + + const result = await worker.recognize(imageData); + + await worker.terminate(); + + return result.data.text; +} diff --git a/src/utils/tts.ts b/src/utils/tts.ts new file mode 100644 index 0000000..c79683b --- /dev/null +++ b/src/utils/tts.ts @@ -0,0 +1,112 @@ +// inspired from https://github.com/open-webui/open-webui/blob/2299f4843003759290cc6bf823595c6578ee4470/src/lib/utils/index.ts + +const CODE_BLOCK_PATTERN = /```[\s\S]*?```/g; + +export const sanitizeEmojis = (text: string): string => { + const EMOJI_PATTERN = /[\uD800-\uDBFF][\uDC00-\uDFFF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g; + return text.replace(EMOJI_PATTERN, ''); +}; + +export const sanitizeMarkdown = (text: string): string => { + return text + .replace(/(```[\s\S]*?```)/g, '') + .replace(/^\|.*\|$/gm, '') + .replace(/(?:\*\*|__)(.*?)(?:\*\*|__)/g, '$1') + .replace(/(?:[*_])(.*?)(?:[*_])/g, '$1') + .replace(/~~(.*?)~~/g, '$1') + .replace(/`([^`]+)`/g, '$1') + .replace(/!?\[([^\]]*)\](?:\([^)]+\)|\[[^\]]*\])/g, '$1') + .replace(/^\[[^\]]+\]:\s*.*$/gm, '') + .replace(/^#{1,6}\s+/gm, '') + .replace(/^\s*[-*+]\s+/gm, '') + .replace(/^\s*(?:\d+\.)\s+/gm, '') + .replace(/^\s*>[> ]*/gm, '') + .replace(/^\s*:\s+/gm, '') + .replace(/\[\^[^\]]*\]/g, '') + .replace(/[-*_~]/g, '') + .replace(/\n{2,}/g, '\n'); +}; + +export const sanitizeText = (content: string): string => { + return sanitizeMarkdown(sanitizeEmojis(content.trim())); +}; + +export const parseTextIntoSentences = (text: string): string[] => { + const codeBlocks: string[] = []; + let blockIndex = 0; + + const processedText = text.replace(CODE_BLOCK_PATTERN, (match) => { + const placeholder = `\u0000${blockIndex}\u0000`; + codeBlocks[blockIndex++] = match; + return placeholder; + }); + + const sentences = processedText.split(/(?<=[.!?])\s+/); + + return sentences + .map(sentence => + sentence.replace(/\u0000(\d+)\u0000/g, (_, idx) => codeBlocks[idx]) + ) + .map(sanitizeText) + .filter(Boolean); +}; + +export const parseTextIntoParagraphs = (text: string): string[] => { + const codeBlocks: string[] = []; + let blockIndex = 0; + + const processedText = text.replace(CODE_BLOCK_PATTERN, (match) => { + const placeholder = `\u0000${blockIndex}\u0000`; + codeBlocks[blockIndex++] = match; + return placeholder; + }); + + return processedText + .split(/\n+/) + .map(paragraph => + paragraph.replace(/\u0000(\d+)\u0000/g, (_, idx) => codeBlocks[idx]) + ) + .map(sanitizeText) + .filter(Boolean); +}; + +export const optimizeSentencesForSpeech = (text: string): string[] => { + return parseTextIntoSentences(text).reduce((optimizedTexts, currentText) => { + const lastIndex = optimizedTexts.length - 1; + + if (lastIndex >= 0) { + const previousText = optimizedTexts[lastIndex]; + const wordCount = previousText.split(/\s+/).length; + const charCount = previousText.length; + + if (wordCount < 4 || charCount < 50) { + optimizedTexts[lastIndex] = `${previousText} ${currentText}`; + } else { + optimizedTexts.push(currentText); + } + } else { + optimizedTexts.push(currentText); + } + + return optimizedTexts; + }, [] as string[]); +}; + +export const splitMessageContent = (content: string, splitBy: string = 'punctuation') => { + const messageContentParts: string[] = []; + + switch (splitBy) { + case 'punctuation': + messageContentParts.push(...optimizeSentencesForSpeech(content)); + break; + case 'paragraph': + messageContentParts.push(...parseTextIntoParagraphs(content)); + break; + case 'none': + messageContentParts.push(sanitizeText(content)); + break; + default: + } + + return messageContentParts; +}; \ No newline at end of file diff --git a/wxt.config.ts b/wxt.config.ts index 69c86bd..0aa73c2 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -45,12 +45,13 @@ export default defineConfig({ } } }), - entrypointsDir: "entries", + entrypointsDir: + process.env.TARGET === "firefox" ? "entries-firefox" : "entries", srcDir: "src", outDir: "build", manifest: { - version: "1.3.9", + version: "1.4.0", name: process.env.TARGET === "firefox" ? "Page Assist - A Web UI for Local AI Models" @@ -85,6 +86,12 @@ export default defineConfig({ } } }, + content_security_policy: + process.env.TARGET !== "firefox" ? + { + extension_pages: + "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';" + } : undefined, permissions: process.env.TARGET === "firefox" ? firefoxMV2Permissions