commit
						066db5dfb7
					
				| @ -84,5 +84,12 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "More Model Settings" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "Summarize", | ||||
|         "explain": "Explain", | ||||
|         "rephrase": "Rephrase", | ||||
|         "translate": "Translate", | ||||
|         "custom": "Custom" | ||||
|     } | ||||
| } | ||||
| @ -29,6 +29,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "Send Notification After Finishing Processing the Knowledge Base" | ||||
|       }, | ||||
|       "generateTitle" :{ | ||||
|         "label": "Generate Title using AI" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -141,6 +144,10 @@ | ||||
|     "option1": "Normal", | ||||
|     "option2": "RAG", | ||||
|     "questionPrompt": "Question Prompt", | ||||
|     "segmented": { | ||||
|       "custom": "Custom Prompts", | ||||
|       "copilot": "Copilot Prompts" | ||||
|     }, | ||||
|     "columns": { | ||||
|       "title": "Title", | ||||
|       "prompt": "Prompt", | ||||
| @ -170,7 +177,8 @@ | ||||
|         "label": "Prompt", | ||||
|         "placeholder": "Enter Prompt", | ||||
|         "required": "Please enter a prompt", | ||||
|         "help": "You can use {key} as variable in your prompt." | ||||
|         "help": "You can use {key} as variable in your prompt.", | ||||
|         "missingTextPlaceholder": "The {text} variable is missing in the prompt. Please add it." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "Is System Prompt" | ||||
|  | ||||
| @ -84,5 +84,11 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "Más Configuraciones del Modelo" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "Resumir", | ||||
|         "explain": "Explicar", | ||||
|         "rephrase": "Reformular", | ||||
|         "translate": "Traducir" | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -29,6 +29,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "Enviar notificación después de terminar el procesamiento de la base de conocimientos" | ||||
|       }, | ||||
|       "generateTitle" :{ | ||||
|         "label": "Generar título usando IA" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -147,6 +150,10 @@ | ||||
|       "type": "Tipo de Prompt", | ||||
|       "actions": "Acciones" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "Invites personnalisées", | ||||
|       "copilot": "Invites Copilot" | ||||
|     }, | ||||
|     "systemPrompt": "Prompt del Sistema", | ||||
|     "quickPrompt": "Prompt Rápido", | ||||
|     "tooltip": { | ||||
| @ -170,7 +177,8 @@ | ||||
|         "label": "Prompt", | ||||
|         "placeholder": "Ingrese un prompt", | ||||
|         "required": "Por favor, ingrese un prompt", | ||||
|         "help": "Puede usar {key} como variable en su prompt." | ||||
|         "help": "Puede usar {key} como variable en su prompt.", | ||||
|         "missingTextPlaceholder": "Falta la variable {text} en el mensaje. Por favor, añádela." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "Es un Prompt del Sistema" | ||||
|  | ||||
							
								
								
									
										13
									
								
								src/assets/locale/fa/chrome.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/assets/locale/fa/chrome.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| { | ||||
|     "heading": "تنظیمات AI کروم", | ||||
|     "status": { | ||||
|         "label": "فعال یا غیر فعال کردن پشتیبانی از AI کروم" | ||||
|     }, | ||||
|     "error": { | ||||
|         "browser_not_supported": "این نسخه از کروم توسط مدل Gemini Nano پشتیبانی نمی شود. لطفا به نسخه ۱۲۷ یا بالاتر به روزرسانی کنید.", | ||||
|         "ai_not_supported": "تنظیم chrome://flags/#prompt-api-for-gemini-nano فعال نشده است. لطفا فعالش کنید..", | ||||
|         "ai_not_ready": "Gemini Nano هنوز آماده نیست; تنظیمات کروم را مجددا بررسی کنید.", | ||||
|         "internal_error": "یک خطای داخلی رخ داد. لطفا بعدا مجددا تلاش نمایید." | ||||
|     }, | ||||
|     "errorDescription": "برای استفاده از AI کروم، به نسخه مرورگر بالاتر از ۱۲۷ احتیاج دارید که اکنون در کانال های Dev و Canary در دسترس است. بعد از دریافت نسخه پشتیبانی شده این گام ها را دنبال کنید: \n\n۱. به `chrome://flags/#prompt-api-for-gemini-nano` بروید و \"Enable\" را انتخاب کنید.\n۲. به `chrome://flags/#optimization-guide-on-device-model` بروید و \"EnabledBypassPrefRequirement\" را انتخاب کنید.\n۳. به `chrome://components` بروید و \"Optimization Guide On Device Model\" را جستجو کنید، بر روی \"Check for Update\" کلیک کنید. این کار مدل را دریافت خواهد کرد. اگر این تنظیمات را نمی بینید، گام های ۱ و ۲ را تکرار و مرورگر را مجددا اجرا کنید." | ||||
| } | ||||
							
								
								
									
										88
									
								
								src/assets/locale/fa/common.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/assets/locale/fa/common.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| { | ||||
|     "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": "ویرایش", | ||||
|     "saveAndSubmit": "ذخیره و ارسال", | ||||
|     "editMessage": { | ||||
|         "placeholder": "یک پیام وارد کنید..." | ||||
|     }, | ||||
|     "submit": "ارسال", | ||||
|     "noData": "اطلاعاتی وجود ندارد", | ||||
|     "noHistory": "تاریخچه گپ وجود ندارد", | ||||
|     "chatWithCurrentPage": "گپ زدن با این صفحه", | ||||
|     "beta": "بتا", | ||||
|     "tts": "بلند بخوان", | ||||
|     "currentChatModelSettings": "تنظیمات مدل گپ فعلی", | ||||
|     "modelSettings": { | ||||
|         "label": "تنظیمات مدل", | ||||
|         "description": "گزینه های مدل را به صورت کلی برای همه گپ ها تنظیم کنید", | ||||
|         "form": { | ||||
|             "keepAlive": { | ||||
|                 "label": "Keep Alive", | ||||
|                 "help": "کنترل می کند که چه مدت مدل پس از درخواست در حافظه باقی می ماند (پیش فرض: 5 دقیقه)", | ||||
|                 "placeholder": "مدت زمان Keep Alive را وارد کنید (مثلا 5m, 10m, 1h)" | ||||
|             }, | ||||
|             "temperature": { | ||||
|                 "label": "Temperature", | ||||
|                 "placeholder": "مقدار Temperature را وارد کنید (مثلا 0.7, 1.0)" | ||||
|             }, | ||||
|             "numCtx": { | ||||
|                 "label": "Number of Contexts", | ||||
|                 "placeholder": "مقدار Number of Contexts را وارد کنید (پیش فرض: 2048)" | ||||
|             }, | ||||
|             "seed": { | ||||
|                 "label": "Seed", | ||||
|                 "placeholder": "مقدار Seed را وارد کنید (e.g. 1234)", | ||||
|                 "help": "تکرارپذیری خروجی مدل" | ||||
|             }, | ||||
|             "topK": { | ||||
|                 "label": "Top K", | ||||
|                 "placeholder": "مقدار Top K را وارد کنید (مثلا 40, 100)" | ||||
|             }, | ||||
|             "topP": { | ||||
|                 "label": "Top P", | ||||
|                 "placeholder": "مقدار Top P را وارد کنید (مثلا 0.9, 0.95)" | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "تنظیمات بیشتر مدل" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/assets/locale/fa/knowledge.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/assets/locale/fa/knowledge.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| { | ||||
|     "addBtn": "افزودن دانش جدید", | ||||
|     "columns": { | ||||
|         "title": "عنوان", | ||||
|         "status": "وضعیت", | ||||
|         "embeddings": "مدل جاسازی", | ||||
|         "createdAt": "ایجاد شده در", | ||||
|         "action": "اقدامات" | ||||
|     }, | ||||
|     "expandedColumns": { | ||||
|         "name": "نام" | ||||
|     }, | ||||
|     "tooltip": { | ||||
|         "delete": "حذف" | ||||
|     }, | ||||
|     "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 اضافه کنید" | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/assets/locale/fa/option.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/assets/locale/fa/option.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| { | ||||
|     "newChat": "گپ جدید", | ||||
|     "selectAPrompt": "یک پرامپت را انتخاب کنید", | ||||
|     "githubRepository": "مخزن GitHub", | ||||
|     "settings": "تنظیمات", | ||||
|     "sidebarTitle": "تاریخچه گپ", | ||||
|     "error": "خطا", | ||||
|     "somethingWentWrong": "مشکلی پیش آمد", | ||||
|     "validationSelectModel": "لطفا یک مدل را برای ادامه انتخاب کنید", | ||||
|     "deleteHistoryConfirmation": "آیا مطمئن هستید که می خواهید این تاریخچه را حذف کنید؟", | ||||
|     "editHistoryTitle": "یک عنوان جدید وارد کنید" | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/assets/locale/fa/playground.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/assets/locale/fa/playground.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| { | ||||
|     "ollamaState": { | ||||
|         "searching": "در حال جستجوی Ollama شما 🦙", | ||||
|         "running": "Ollama در حال اجرا است 🦙", | ||||
|         "notRunning": "امکان اتصال به Ollama وجود ندارد 🦙", | ||||
|         "connectionError": "به نظر می رسد که خطای اتصال دارید. لطفا برای عیب یابی به این <anchor>مستندات</anchor> مراجعه کنید." | ||||
|     }, | ||||
|     "formError": { | ||||
|         "noModel": "لطفا یک مدل را انتخاب کنید", | ||||
|         "noEmbeddingModel": "لطفا یک مدل جاسازی در صفحه تنظیمات > RAG تنظیم کنید" | ||||
|     }, | ||||
|     "form": { | ||||
|         "textarea": { | ||||
|             "placeholder": "یک پیام تایپ کنید..." | ||||
|         }, | ||||
|         "webSearch": { | ||||
|             "on": "روشن", | ||||
|             "off": "خاموش" | ||||
|         } | ||||
|     }, | ||||
|     "tooltip": { | ||||
|         "searchInternet": "جستجوی اینترنت", | ||||
|         "speechToText": "گفتار به متن", | ||||
|         "uploadImage": "آپلود تصویر", | ||||
|         "stopStreaming": "توقف Streaming", | ||||
|         "knowledge": "دانش" | ||||
|     }, | ||||
|     "sendWhenEnter": "با فشار دادن Enter ارسال شود" | ||||
| } | ||||
							
								
								
									
										335
									
								
								src/assets/locale/fa/settings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								src/assets/locale/fa/settings.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,335 @@ | ||||
| { | ||||
|   "generalSettings": { | ||||
|     "title": "تنظیمات عمومی", | ||||
|     "settings": { | ||||
|       "heading": "تنظیمات رابط کاربری وب", | ||||
|       "speechRecognitionLang": { | ||||
|         "label": "زبان تشخیص گفتار", | ||||
|         "placeholder": "یک زبان را انتخاب کنید" | ||||
|       }, | ||||
|       "language": { | ||||
|         "label": "زبان", | ||||
|         "placeholder": "یک زبان را انتخاب کنید" | ||||
|       }, | ||||
|       "darkMode": { | ||||
|         "label": "تغییر تم", | ||||
|         "options": { | ||||
|           "light": "روشن", | ||||
|           "dark": "تیره" | ||||
|         } | ||||
|       }, | ||||
|       "copilotResumeLastChat": { | ||||
|         "label": "آخرین گفتگو را هنگام باز کردن SidePanel (Copilot) از سر بگیر" | ||||
|       }, | ||||
|       "hideCurrentChatModelSettings": { | ||||
|         "label": "مخفی کردن تنظیمات مدل گپ فعلی را" | ||||
|       }, | ||||
|       "restoreLastChatModel": { | ||||
|         "label": "بازیابی آخرین مدل استفاده شده برای گپهای قبلی" | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "ارسال نوتیفیکیشن پس از اتمام پردازش پایگاه دانش" | ||||
|       }, | ||||
|       "generateTitle" :{ | ||||
|         "label": "تولید عنوان با استفاده از هوش مصنوعی" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
|       "heading": "تنظیمات گپ Copilot با وب سایت", | ||||
|       "ragEnabled": { | ||||
|         "label": "چت با وب سایت با استفاده از بردارهای جاسازی" | ||||
|       }, | ||||
|       "maxWebsiteContext": { | ||||
|         "label": "اندازه محتوای وب سایت حالت عادی", | ||||
|         "placeholder": "اندازه محتوا (پیش فرض 4028)" | ||||
|       } | ||||
|     }, | ||||
|     "webSearch": { | ||||
|       "heading": "مدیریت جستجوی وب", | ||||
|       "searchMode": { | ||||
|         "label": "انجام جستجوی ساده اینترنتی" | ||||
|       }, | ||||
|       "provider": { | ||||
|         "label": "موتور جستجو", | ||||
|         "placeholder": "یک موتور جستجو را انتخاب کنید" | ||||
|       }, | ||||
|       "totalSearchResults": { | ||||
|         "label": "مجموع نتایج جستجو", | ||||
|         "placeholder": "کل نتایج جستجو را وارد کنید" | ||||
|       }, | ||||
|       "visitSpecificWebsite": { | ||||
|         "label": "مراجعه به وب سایت ذکر شده در پیام" | ||||
|       } | ||||
|     }, | ||||
|     "system": { | ||||
|       "heading": "تنظیمات سیستم", | ||||
|       "deleteChatHistory": { | ||||
|         "label": "حذف تاریخچه گفتگو", | ||||
|         "button": "حذف", | ||||
|         "confirm": "آیا مطمئن هستید که می خواهید تاریخچه گفتگوهای خود را حذف کنید؟ این عمل قابل برگشت نیست." | ||||
|       }, | ||||
|       "export": { | ||||
|         "label": "تاریخچه گپ، پایگاه دانش و پرامپتها را اکسپورت کنید", | ||||
|         "button": "اکسپورت دادهها", | ||||
|         "success": "موفقیت در اکسپورت" | ||||
|       }, | ||||
|       "import": { | ||||
|         "label": "تاریخچه گپ، پایگاه دانش و پرامپتها را ایمپورت کنید", | ||||
|         "button": "ایمپورت دادهها", | ||||
|         "success": "موفقیت در ایمپورت", | ||||
|         "error": "خطا در ایمپورت" | ||||
|       } | ||||
|     }, | ||||
|     "tts": { | ||||
|       "heading": "تنظیمات تبدیل متن به گفتار", | ||||
|       "ttsEnabled": { | ||||
|         "label": "فعال کردن تبدیل متن به گفتار" | ||||
|       }, | ||||
|       "ttsProvider": { | ||||
|         "label": "ارائه دهنده تبدیل متن به گفتار", | ||||
|         "placeholder": "یک ارائه دهنده را انتخاب کنید" | ||||
|       }, | ||||
|       "ttsVoice": { | ||||
|         "label": "صدای تبدیل متن به گفتار", | ||||
|         "placeholder": "صدا را انتخاب کنید" | ||||
|       }, | ||||
|       "ssmlEnabled": { | ||||
|         "label": "فعال کردن SSML (Speech Synthesis Markup Language)" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "manageModels": { | ||||
|     "title": "مدیریت مدلها", | ||||
|     "addBtn": "افزودن مدل جدید", | ||||
|     "columns": { | ||||
|       "name": "نام", | ||||
|       "digest": "هضم", | ||||
|       "modifiedAt": "اصلاح شده در", | ||||
|       "size": "اندازه", | ||||
|       "actions": "اقدامات" | ||||
|     }, | ||||
|     "expandedColumns": { | ||||
|       "parentModel": "مدل والد", | ||||
|       "format": "فرمت", | ||||
|       "family": "خانواده", | ||||
|       "parameterSize": "اندازه پارامترها", | ||||
|       "quantizationLevel": "سطح کوانتیزاسیون" | ||||
|     }, | ||||
|     "tooltip": { | ||||
|       "delete": "حذف مدل", | ||||
|       "repull": "دریافت دوباره مدل" | ||||
|     }, | ||||
|     "confirm": { | ||||
|       "delete": "آیا مطمئن هستید که می خواهید این مدل را حذف کنید؟", | ||||
|       "repull": "آیا مطمئن هستید که می خواهید این مدل را دوباره دریافت کنید؟" | ||||
|     }, | ||||
|     "modal": { | ||||
|       "title": "افزودن مدل جدید", | ||||
|       "placeholder": "نام مدل را وارد کنید", | ||||
|       "pull": "دریافت مدل" | ||||
|     }, | ||||
|     "notification": { | ||||
|       "pullModel": "در حال دریافت مدل", | ||||
|       "pullModelDescription": "در حال دریافت مدل {{modelName}}. برای جزئیات بیشتر، آیکون افزونه را بررسی کنید.", | ||||
|       "success": "موفقیت", | ||||
|       "error": "خطا", | ||||
|       "successDescription": "مدل با موفقیت دریافت شد", | ||||
|       "successDeleteDescription": "مدل با موفقیت حذف شد", | ||||
|       "someError": "مشکلی پیش آمد. لطفا بعدا دوباره امتحان کنید" | ||||
|     } | ||||
|   }, | ||||
|   "managePrompts": { | ||||
|     "title": "مدیریت پرامپتها", | ||||
|     "addBtn": "اضافه کردن پرامپت جدید", | ||||
|     "option1": "عادی", | ||||
|     "option2": "RAG", | ||||
|     "questionPrompt": "سوال پرامپت", | ||||
|     "columns": { | ||||
|       "title": "عنولن", | ||||
|       "prompt": "پرامپت", | ||||
|       "type": "نوع پرامپت", | ||||
|       "actions": "اقدامات" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "پرامپتهای سفارشی", | ||||
|       "copilot": "پرامپتهای کوپایلوت" | ||||
|     }, | ||||
|     "systemPrompt": "پرامپت سیستم ", | ||||
|     "quickPrompt": "پرامپت سریع", | ||||
|     "tooltip": { | ||||
|       "delete": "پاک کردن پرامپت", | ||||
|       "edit": "ویرایش پرامپت" | ||||
|     }, | ||||
|     "confirm": { | ||||
|       "delete": "آیا مطمئن هستید که می خواهید این پرامپت را حذف کنید؟ این عمل قابل برگشت نیست." | ||||
|     }, | ||||
|     "modal": { | ||||
|       "addTitle": "اضافه کردن پرامپت جدید", | ||||
|       "editTitle": "ویرایش پرامپت" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "عنوان", | ||||
|         "placeholder": "پرامپت عالی من", | ||||
|         "required": "لطفا یک عنوان وارد کنید" | ||||
|       }, | ||||
|       "prompt": { | ||||
|         "label": "پرامپت", | ||||
|         "placeholder": "پرامپت وارد کنید", | ||||
|         "required": "لطفا یک پرامپت وارد کنید", | ||||
|         "help": "می توانید از {key} به عنوان متغیر در درخواست خود استفاده کنید." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "یک پرامپت سیستمی باشد" | ||||
|       }, | ||||
|       "btnSave": { | ||||
|         "saving": "در حال اضافه کردن پرامپت...", | ||||
|         "save": "اضافه کردن پرامپت" | ||||
|       }, | ||||
|       "btnEdit": { | ||||
|         "saving": "در حال به روزرسانی پرامپت...", | ||||
|         "save": "به روزرسانی پرامپت" | ||||
|       } | ||||
|     }, | ||||
|     "notification": { | ||||
|       "addSuccess": "پرامپت اضافه شد", | ||||
|       "addSuccessDesc": "پرامپت با موفقیت اضافه شد", | ||||
|       "error": "خطا", | ||||
|       "someError": "مشکلی پیش آمد. لطفا بعدا دوباره امتحان کنید", | ||||
|       "updatedSuccess": "پرامپت به روز شد", | ||||
|       "updatedSuccessDesc": "پرامپت با موفقیت به روز شد", | ||||
|       "deletedSuccess": "پرامپت حذف شد", | ||||
|       "deletedSuccessDesc": "پرامپت با موفقیت حذف شد" | ||||
|     } | ||||
|   }, | ||||
|   "manageShare": { | ||||
|     "title": "مدیریت اشتراک گذاری", | ||||
|     "heading": "پیکربندی URL اشتراک گذاری صفحه", | ||||
|     "form": { | ||||
|       "url": { | ||||
|         "label": "آدرس اشتراک گذاری صفحه", | ||||
|         "placeholder": "URL اشتراک گذاری صفحه را وارد کنید", | ||||
|         "required": "لطفا URL اشتراک گذاری صفحه خود را وارد کنید!", | ||||
|         "help": "به دلایل حفظ حریم خصوصی، می توانید اشتراک گذاری صفحه را خودتان میزبانی کنید و URL را در اینجا ارائه دهید. <anchor>بیشتر بدانید</anchor>." | ||||
|       } | ||||
|     }, | ||||
|     "webshare": { | ||||
|       "heading": "اشتراک گذاری وب", | ||||
|       "columns": { | ||||
|         "title": "عنوان", | ||||
|         "url": "URL", | ||||
|         "actions": "اقدامات" | ||||
|       }, | ||||
|       "tooltip": { | ||||
|         "delete": "حذف اشتراک گذاری" | ||||
|       }, | ||||
|       "confirm": { | ||||
|         "delete": "آیا مطمئن هستید که می خواهید این اشتراک گذاری را حذف کنید؟ این عمل قابل برگشت نیست." | ||||
|       }, | ||||
|       "label": "مدیریت اشتراک گذاری صفحه", | ||||
|       "description": "ویژگی اشتراک گذاری صفحه را فعال یا غیرفعال کنید" | ||||
|     }, | ||||
|     "notification": { | ||||
|       "pageShareSuccess": "URL اشتراک گذاری صفحه با موفقیت به روز شد", | ||||
|       "someError": "مشکلی پیش آمد. لطفا بعدا دوباره امتحان کنید", | ||||
|       "webShareDeleteSuccess": "اشتراک گذاری وب با موفقیت حذف شد" | ||||
|     } | ||||
|   }, | ||||
|   "ollamaSettings": { | ||||
|     "title": "تنظیمات Ollama", | ||||
|     "heading": "پیکربندی Ollama", | ||||
|     "settings": { | ||||
|       "ollamaUrl": { | ||||
|         "label": "آدرس Ollama", | ||||
|         "placeholder": "URL Ollama را وارد کنید" | ||||
|       }, | ||||
|       "advanced": { | ||||
|         "label": "پیکربندی پیشرفته URL Ollama", | ||||
|         "urlRewriteEnabled": { | ||||
|           "label": "URL مبدا سفارشی را فعال یا غیرفعال کنید" | ||||
|         }, | ||||
|         "rewriteUrl": { | ||||
|           "label": "URL مبدا سفارشی", | ||||
|           "placeholder": "URL مبدا سفارشی را وارد کنید" | ||||
|         }, | ||||
|         "headers": { | ||||
|           "label": "هدرهای سفارشی", | ||||
|           "add": "افزودن هدر", | ||||
|           "key": { | ||||
|             "label": "کلید هدر", | ||||
|             "placeholder": "Authorization" | ||||
|           }, | ||||
|           "value": { | ||||
|             "label": "مقدار هدر", | ||||
|             "placeholder": "Bearer token" | ||||
|           } | ||||
|         }, | ||||
|         "help": "اگر با Ollama در Page Assist مشکل اتصال دارید، می توانید یک URL اصلی سفارشی پیکربندی کنید. برای کسب اطلاعات بیشتر در مورد پیکربندی، <anchor>اینجا را کلیک</anchor> کنید." | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "manageSearch": { | ||||
|     "title": "مدیریت جستجوی وب", | ||||
|     "heading": "پیکربندی جستجوی وب" | ||||
|   }, | ||||
|   "about": { | ||||
|     "title": "درباره", | ||||
|     "heading": "درباره", | ||||
|     "chromeVersion": "نسخه دستیار صفحه", | ||||
|     "ollamaVersion": "نسخه Ollama", | ||||
|     "support": "شما می توانید با کمک مالی یا حمایت مالی از طریق پلتفرم های زیر از پروژه Page Assist حمایت کنید:", | ||||
|     "koFi": "پشتیبانی در Ko-fi", | ||||
|     "githubSponsor": "حمایت مالی در GitHub", | ||||
|     "githubRepo": "مخزن GitHub" | ||||
|   }, | ||||
|   "manageKnowledge": { | ||||
|     "title": "مدیریت دانش", | ||||
|     "heading": "پیکربندی پایگاه دانش" | ||||
|   }, | ||||
|   "rag": { | ||||
|     "title": "تنظیمات RAG", | ||||
|     "ragSettings": { | ||||
|       "label": "تنظیمات RAG", | ||||
|       "model": { | ||||
|         "label": "مدل جاسازی", | ||||
|         "required": "لطفا یک مدل را انتخاب کنید", | ||||
|         "help": "به شدت توصیه می شود از مدل های جاسازی مانند `nomic-embed-text` استفاده کنید.", | ||||
|         "placeholder": "یک مدل را انتخاب کنید" | ||||
|       }, | ||||
|       "chunkSize": { | ||||
|         "label": "اندازه تکه", | ||||
|         "placeholder": "اندازه تکه را وارد کنید", | ||||
|         "required": "لطفا اندازه تکه را وارد کنید" | ||||
|       }, | ||||
|       "chunkOverlap": { | ||||
|         "label": "همپوشانی تکه", | ||||
|         "placeholder": "داخل تکه همپوشانی شوید", | ||||
|         "required": "لطفا یک همپوشانی تکه ای وارد کنید" | ||||
|       }, | ||||
|       "totalFilePerKB": { | ||||
|         "label": "محدودیت فایل پیش فرض پایگاه دانش", | ||||
|         "placeholder": "محدودیت فایل پیش فرض را وارد کنید (به عنوان مثال، 10)", | ||||
|         "required": "لطفا محدودیت پیش فرض فایل را وارد کنید" | ||||
|       } | ||||
|     }, | ||||
|     "prompt": { | ||||
|       "label": "پیکربندی پرامپت RAG", | ||||
|       "option1": "عادی", | ||||
|       "option2": "وب", | ||||
|       "alert": "پیکربندی اعلان سیستم در اینجا منسوخ شده است. لطفا از بخش مدیریت پرامپتها برای افزودن یا ویرایش درخواستها استفاده کنید. این بخش در نسخه بعدی حذف خواهد شد", | ||||
|       "systemPrompt": "پرامپت سیستم", | ||||
|       "systemPromptPlaceholder": "پرامپت سیستم را وارد کنید", | ||||
|       "webSearchPrompt": "پرامپت جستجوی وب", | ||||
|       "webSearchPromptHelp": "«{search_results}» را از پرامپت حذف نکنید.", | ||||
|       "webSearchPromptError": "لطفا یک پرامپت جستجوی وب وارد کنید", | ||||
|       "webSearchPromptPlaceholder": "پرامپت جستجوی وب را وارد کنید", | ||||
|       "webSearchFollowUpPrompt": "پرامپت پیگیری جستجوی وب", | ||||
|       "webSearchFollowUpPromptHelp": "«{chat_history}» و «{question}» را از پرامپت حذف نکنید.", | ||||
|       "webSearchFollowUpPromptError": "لطفا پرامپت پیگیری جستجوی وب خود را وارد کنید!", | ||||
|       "webSearchFollowUpPromptPlaceholder": "پرامپت پیگیری جستجوی وب شما" | ||||
|     } | ||||
|   }, | ||||
|   "chromeAiSettings": { | ||||
|     "title": "تنظیمات هوش مصنوعی کروم" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/assets/locale/fa/sidepanel.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/assets/locale/fa/sidepanel.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| { | ||||
|     "tooltip": { | ||||
|         "embed": "ممکن است چند دقیقه طول بکشد تا صفحه جاسازی شود. لطفاً منتظر بمانید...", | ||||
|         "clear": "پاک کردن تاریخچه گپ", | ||||
|         "history": "تاریخچه گپ" | ||||
|     } | ||||
| } | ||||
| @ -51,7 +51,7 @@ | ||||
|     "chatWithCurrentPage": "Discuter avec la page actuelle", | ||||
|     "beta": "Bêta", | ||||
|     "tts": "Synthèse vocale", | ||||
|     "currentChatModelSettings":"Paramètres actuels du modèle de chat", | ||||
|     "currentChatModelSettings": "Paramètres actuels du modèle de chat", | ||||
|     "modelSettings": { | ||||
|         "label": "Paramètres du modèle", | ||||
|         "description": "Définissez les options de modèle globale pour tous les chats", | ||||
| @ -84,5 +84,11 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "Plus de paramètres du modèle" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "Résumer", | ||||
|         "explain": "Expliquer", | ||||
|         "rephrase": "Reformuler", | ||||
|         "translate": "Traduire" | ||||
|     } | ||||
| } | ||||
| @ -29,6 +29,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "Envoyer une notification après avoir terminé le traitement de la base de connaissances" | ||||
|       },  | ||||
|       "generateTitle" :{ | ||||
|         "label": "Générer le titre en utilisant l'IA" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -160,6 +163,10 @@ | ||||
|       "addTitle": "Ajouter un nouveau prompt", | ||||
|       "editTitle": "Modifier le prompt" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "Invites personnalisées", | ||||
|       "copilot": "Invites Copilot" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "Titre", | ||||
| @ -170,7 +177,8 @@ | ||||
|         "label": "Prompt", | ||||
|         "placeholder": "Entrer Prompt", | ||||
|         "required": "Veuillez entrer un prompt", | ||||
|         "help": "Vous pouvez utiliser {key} comme variable dans votre prompt." | ||||
|         "help": "Vous pouvez utiliser {key} comme variable dans votre prompt.", | ||||
|         "missingTextPlaceholder": "La variable {text} est manquante dans le message. Veuillez l'ajouter." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "Est un prompt système" | ||||
|  | ||||
| @ -84,5 +84,11 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "Altre Impostazioni del Modello" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "Riassumere", | ||||
|         "explain": "Spiegare", | ||||
|         "rephrase": "Riformulare", | ||||
|         "translate": "Tradurre" | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -29,6 +29,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "Inviare notifica dopo aver terminato l'elaborazione della base di conoscenza" | ||||
|       },  | ||||
|       "generateTitle" :{ | ||||
|         "label": "Genera titolo utilizzando l'IA" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -160,6 +163,10 @@ | ||||
|       "addTitle": "Aggiungi Nuovo Prompt", | ||||
|       "editTitle": "Modifica Prompt" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "Prompt personalizzati", | ||||
|       "copilot": "Prompt Copilot" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "Titolo", | ||||
| @ -170,7 +177,8 @@ | ||||
|         "label": "Prompt", | ||||
|         "placeholder": "Inserisci Prompt", | ||||
|         "required": "Scrivi il prompt", | ||||
|         "help": "Puoi usare {key} come variabile nel tuo prompt." | ||||
|         "help": "Puoi usare {key} come variabile nel tuo prompt.", | ||||
|         "missingTextPlaceholder": "La variabile {text} manca nel prompt. Per favore, aggiungila." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "Prompt di Sistema" | ||||
|  | ||||
| @ -1,88 +1,94 @@ | ||||
| { | ||||
|     "pageAssist": "ページアシスト", | ||||
|     "selectAModel": "モデルを選択", | ||||
|     "save": "保存", | ||||
|     "saved": "保存済み", | ||||
|     "cancel": "キャンセル", | ||||
|     "retry": "再試行", | ||||
|     "share": { | ||||
|       "tooltip": { | ||||
|         "share": "共有" | ||||
|   "pageAssist": "ページアシスト", | ||||
|   "selectAModel": "モデルを選択", | ||||
|   "save": "保存", | ||||
|   "saved": "保存済み", | ||||
|   "cancel": "キャンセル", | ||||
|   "retry": "再試行", | ||||
|   "share": { | ||||
|     "tooltip": { | ||||
|       "share": "共有" | ||||
|     }, | ||||
|     "modal": { | ||||
|       "title": "チャットリンクを共有" | ||||
|     }, | ||||
|     "form": { | ||||
|       "defaultValue": { | ||||
|         "name": "匿名", | ||||
|         "title": "無題のチャット" | ||||
|       }, | ||||
|       "modal": { | ||||
|         "title": "チャットリンクを共有" | ||||
|       "title": { | ||||
|         "label": "チャットタイトル", | ||||
|         "placeholder": "チャットタイトルを入力", | ||||
|         "required": "チャットタイトルは必須です" | ||||
|       }, | ||||
|       "form": { | ||||
|         "defaultValue": { | ||||
|           "name": "匿名", | ||||
|           "title": "無題のチャット" | ||||
|         }, | ||||
|         "title": { | ||||
|           "label": "チャットタイトル", | ||||
|           "placeholder": "チャットタイトルを入力", | ||||
|           "required": "チャットタイトルは必須です" | ||||
|         }, | ||||
|         "name": { | ||||
|           "label": "あなたの名前", | ||||
|           "placeholder": "名前を入力", | ||||
|           "required": "名前は必須です" | ||||
|         }, | ||||
|         "btn": { | ||||
|           "save": "リンクを生成", | ||||
|           "saving": "リンクを生成中..." | ||||
|         } | ||||
|       "name": { | ||||
|         "label": "あなたの名前", | ||||
|         "placeholder": "名前を入力", | ||||
|         "required": "名前は必須です" | ||||
|       }, | ||||
|       "notification": { | ||||
|         "successGenerate": "リンクがクリップボードにコピーされました", | ||||
|         "failGenerate": "リンクの生成に失敗しました" | ||||
|       "btn": { | ||||
|         "save": "リンクを生成", | ||||
|         "saving": "リンクを生成中..." | ||||
|       } | ||||
|     }, | ||||
|     "copyToClipboard": "クリップボードにコピー", | ||||
|     "webSearch": "ウェブを検索中", | ||||
|     "regenerate": "再生成", | ||||
|     "edit": "編集", | ||||
|     "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)" | ||||
|             }, | ||||
|             "seed": { | ||||
|                 "label": "シード", | ||||
|                 "placeholder": "シード値を入力してください(例:1234)", | ||||
|                 "help": "モデル出力の再現性" | ||||
|             }, | ||||
|             "topK": { | ||||
|                 "label": "Top K", | ||||
|                 "placeholder": "Top K値を入力してください(例:40、100)" | ||||
|             }, | ||||
|             "topP": { | ||||
|                 "label": "Top P", | ||||
|                 "placeholder": "Top P値を入力してください(例:0.9、0.95)" | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "その他のモデル設定" | ||||
|     "notification": { | ||||
|       "successGenerate": "リンクがクリップボードにコピーされました", | ||||
|       "failGenerate": "リンクの生成に失敗しました" | ||||
|     } | ||||
|   } | ||||
|   }, | ||||
|   "copyToClipboard": "クリップボードにコピー", | ||||
|   "webSearch": "ウェブを検索中", | ||||
|   "regenerate": "再生成", | ||||
|   "edit": "編集", | ||||
|   "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)" | ||||
|       }, | ||||
|       "seed": { | ||||
|         "label": "シード", | ||||
|         "placeholder": "シード値を入力してください(例:1234)", | ||||
|         "help": "モデル出力の再現性" | ||||
|       }, | ||||
|       "topK": { | ||||
|         "label": "Top K", | ||||
|         "placeholder": "Top K値を入力してください(例:40、100)" | ||||
|       }, | ||||
|       "topP": { | ||||
|         "label": "Top P", | ||||
|         "placeholder": "Top P値を入力してください(例:0.9、0.95)" | ||||
|       } | ||||
|     }, | ||||
|     "advanced": "その他のモデル設定" | ||||
|   }, | ||||
|   "copilot": { | ||||
|     "summary": "要約", | ||||
|     "explain": "説明", | ||||
|     "rephrase": "言い換え", | ||||
|     "translate": "翻訳" | ||||
|   } | ||||
| } | ||||
| @ -32,6 +32,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "ナレッジベースの処理完了後に通知を送信" | ||||
|       }, | ||||
|       "generateTitle": { | ||||
|         "label": "AIを使用してタイトルを生成" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -163,6 +166,10 @@ | ||||
|       "addTitle": "新しいプロンプトを追加", | ||||
|       "editTitle": "プロンプトを編集" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "カスタムプロンプト", | ||||
|       "copilot": "Copilotプロンプト" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "タイトル", | ||||
| @ -173,7 +180,8 @@ | ||||
|         "label": "プロンプト", | ||||
|         "placeholder": "プロンプトを入力", | ||||
|         "required": "プロンプトを入力してください", | ||||
|         "help": "プロンプト内で{key}を変数として使用できます。" | ||||
|         "help": "プロンプト内で{key}を変数として使用できます。", | ||||
|         "missingTextPlaceholder": "プロンプトに{text}変数がありません。追加してください。" | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "システムプロンプト" | ||||
|  | ||||
| @ -83,5 +83,11 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "കൂടുതൽ മോഡൽ ക്രമീകരണങ്ങൾ" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "സംഗ്രഹിക്കുക", | ||||
|         "explain": "വിശദീകരിക്കുക", | ||||
|         "rephrase": "പുനഃരൂപീകരിക്കുക", | ||||
|         "translate": "വിവർത്തനം ചെയ്യുക" | ||||
|     } | ||||
| } | ||||
| @ -32,6 +32,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "അറിവ് ശേഖരം പ്രോസസ്സ് ചെയ്ത് കഴിഞ്ഞതിന് ശേഷം അറിയിപ്പ് അയയ്ക്കുക" | ||||
|       }, | ||||
|       "generateTitle": { | ||||
|         "label": "എഐ ഉപയോഗിച്ച് ശീർഷകം സൃഷ്ടിക്കുക" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -163,6 +166,10 @@ | ||||
|       "addTitle": "പുതിയ പ്രോംപ്റ്റ് ചേര്ക്കുക", | ||||
|       "editTitle": "പ്രോംപ്റ്റ് എഡിറ്റുചെയ്യുക" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "കസ്റ്റം പ്രോംപ്റ്റുകൾ", | ||||
|       "copilot": "കോപൈലറ്റ് പ്രോംപ്റ്റുകൾ" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "തലക്കെട്ട്", | ||||
| @ -173,7 +180,8 @@ | ||||
|         "label": "പ്രോംപ്റ്റ്", | ||||
|         "placeholder": "പ്രോംപ്റ്റ് നല്കുക", | ||||
|         "required": "ദയവായി ഒരു പ്രോംപ്റ്റ് നല്കുക", | ||||
|         "help": "നിങ്ങള്ക്ക് {key} എന്ന രീതിയില് പ്രോംപ്റ്റില് വേരിയബിളുകള് ഉപയോഗിക്കാവുന്നതാണ്." | ||||
|         "help": "നിങ്ങള്ക്ക് {key} എന്ന രീതിയില് പ്രോംപ്റ്റില് വേരിയബിളുകള് ഉപയോഗിക്കാവുന്നതാണ്.", | ||||
|         "missingTextPlaceholder": "പ്രോംപ്റ്റിൽ {text} വേരിയബിൾ കാണുന്നില്ല. ദയവായി അത് ചേർക്കുക." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "സിസ്റ്റം പ്രോംപ്റ്റ് ആണോ" | ||||
|  | ||||
| @ -84,5 +84,11 @@ | ||||
|       } | ||||
|     }, | ||||
|     "advanced": "Mais Configurações do Modelo" | ||||
|   }, | ||||
|   "copilot": { | ||||
|     "summary": "Resumir", | ||||
|     "explain": "Explicar", | ||||
|     "rephrase": "Reformular", | ||||
|     "translate": "Traduzir" | ||||
|   } | ||||
| } | ||||
| @ -29,6 +29,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "Enviar notificação após concluir o processamento da base de conhecimento" | ||||
|       }, | ||||
|       "generateTitle": { | ||||
|         "label": "Gerar título usando IA" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -160,6 +163,10 @@ | ||||
|       "addTitle": "Adicionar Novo Prompt", | ||||
|       "editTitle": "Editar Prompt" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "Prompts personalizados", | ||||
|       "copilot": "Prompts do Copilot" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "Título", | ||||
| @ -170,7 +177,8 @@ | ||||
|         "label": "Prompt", | ||||
|         "placeholder": "Digite o Prompt", | ||||
|         "required": "Por favor, insira um prompt", | ||||
|         "help": "Você pode usar {key} como variável em seu prompt." | ||||
|         "help": "Você pode usar {key} como variável em seu prompt.", | ||||
|         "missingTextPlaceholder": "A variável {text} está faltando no prompt. Por favor, adicione-a." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "É um Prompt do Sistema" | ||||
|  | ||||
| @ -84,5 +84,11 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "Больше настроек модели" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "Обобщить", | ||||
|         "explain": "Объяснить", | ||||
|         "rephrase": "Перефразировать", | ||||
|         "translate": "Перевести" | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -29,6 +29,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "Отправить уведомление после завершения обработки базы знаний" | ||||
|       }, | ||||
|       "generateTitle": { | ||||
|         "label": "Сгенерировать заголовок с помощью ИИ" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -161,6 +164,10 @@ | ||||
|       "addTitle": "Добавить новую подсказку", | ||||
|       "editTitle": "Редактировать подсказку" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "Пользовательские подсказки", | ||||
|       "copilot": "Подсказки Copilot" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "Название", | ||||
| @ -171,7 +178,8 @@ | ||||
|         "label": "Подсказка", | ||||
|         "placeholder": "Введите подсказку", | ||||
|         "required": "Пожалуйста, введите подсказку", | ||||
|         "help": "Вы можете использовать {key} в качестве переменной в своей подсказке." | ||||
|         "help": "Вы можете использовать {key} в качестве переменной в своей подсказке.", | ||||
|         "missingTextPlaceholder": "Переменная {text} отсутствует в подсказке. Пожалуйста, добавьте ее." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "Это системная подсказка" | ||||
|  | ||||
| @ -84,5 +84,11 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "更多模型设置" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "总结", | ||||
|         "explain": "解释", | ||||
|         "rephrase": "重述", | ||||
|         "translate": "翻译" | ||||
|     } | ||||
| } | ||||
| @ -32,6 +32,9 @@ | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "完成知识库处理后发送通知" | ||||
|       }, | ||||
|       "generateTitle": { | ||||
|         "label": "使用人工智能生成标题" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
| @ -163,6 +166,10 @@ | ||||
|       "addTitle": "添加新提示词", | ||||
|       "editTitle": "编辑提示词" | ||||
|     }, | ||||
|     "segmented": { | ||||
|       "custom": "自定义提示", | ||||
|       "copilot": "Copilot 提示" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "标题", | ||||
| @ -174,7 +181,8 @@ | ||||
|         "label": "提示词", | ||||
|         "placeholder": "输入提示词", | ||||
|         "required": "请输入提示词", | ||||
|         "help": "您可以在提示词中使用 {key} 作为变量。" | ||||
|         "help": "您可以在提示词中使用 {key} 作为变量。", | ||||
|         "missingTextPlaceholder": "提示中缺少{text}变量。请添加它。" | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "是否为系统提示词" | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import Markdown from "../../Common/Markdown" | ||||
| import React from "react" | ||||
| import { Image, Tooltip } from "antd" | ||||
| import { Tag, Image, Tooltip } from "antd" | ||||
| import { WebSearch } from "./WebSearch" | ||||
| import { | ||||
|   CheckIcon, | ||||
| @ -14,9 +14,11 @@ import { EditMessageForm } from "./EditMessageForm" | ||||
| import { useTranslation } from "react-i18next" | ||||
| import { MessageSource } from "./MessageSource" | ||||
| import { useTTS } from "@/hooks/useTTS" | ||||
| import { tagColors } from "@/utils/color" | ||||
| 
 | ||||
| type Props = { | ||||
|   message: string | ||||
|   message_type?: string | ||||
|   hideCopy?: boolean | ||||
|   botAvatar?: JSX.Element | ||||
|   userAvatar?: JSX.Element | ||||
| @ -76,13 +78,23 @@ export const PlaygroundMessage = (props: Props) => { | ||||
|             props.currentMessageIndex === props.totalMessages - 1 ? ( | ||||
|               <WebSearch /> | ||||
|             ) : null} | ||||
| 
 | ||||
|             <div> | ||||
|               {props?.message_type && ( | ||||
|                 <Tag color={tagColors[props?.message_type] || "default"}> | ||||
|                   {t(`copilot.${props?.message_type}`)} | ||||
|                 </Tag> | ||||
|               )} | ||||
|             </div> | ||||
|             <div className="flex flex-grow flex-col"> | ||||
|               {!editMode ? ( | ||||
|                 props.isBot ? ( | ||||
|                   <Markdown message={props.message} /> | ||||
|                 ) : ( | ||||
|                   <p className="prose dark:prose-invert whitespace-pre-line	 prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"> | ||||
|                   <p | ||||
|                     className={`prose dark:prose-invert whitespace-pre-line	 prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark ${ | ||||
|                       props.message_type && | ||||
|                       "italic text-gray-500 dark:text-gray-400 text-sm" | ||||
|                     }`}>
 | ||||
|                     {props.message} | ||||
|                   </p> | ||||
|                 ) | ||||
|  | ||||
| @ -14,7 +14,6 @@ import { useTranslation } from "react-i18next" | ||||
| import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect" | ||||
| import { useSpeechRecognition } from "@/hooks/useSpeechRecognition" | ||||
| import { PiGlobe } from "react-icons/pi" | ||||
| import { extractReadabilityContent } from "@/parser/reader" | ||||
| 
 | ||||
| type Props = { | ||||
|   dropedFile: File | undefined | ||||
| @ -39,15 +38,26 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { | ||||
|     selectedKnowledge | ||||
|   } = useMessageOption() | ||||
| 
 | ||||
|   const isMobile = () => { | ||||
|     return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( | ||||
|       navigator.userAgent | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   const textAreaFocus = () => { | ||||
|     if (textareaRef.current) { | ||||
|       if ( | ||||
|         textareaRef.current.selectionStart === textareaRef.current.selectionEnd | ||||
|       ) { | ||||
|         textareaRef.current.focus() | ||||
|         if (!isMobile()) { | ||||
|           textareaRef.current.focus() | ||||
|         } else { | ||||
|           textareaRef.current.blur() | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   const form = useForm({ | ||||
|     initialValues: { | ||||
|       message: "", | ||||
|  | ||||
| @ -8,16 +8,18 @@ import { | ||||
|   Input, | ||||
|   Form, | ||||
|   Switch, | ||||
|   Segmented, | ||||
|   Tag | ||||
| } from "antd" | ||||
| import { Trash2, Pen, Computer, Zap } from "lucide-react" | ||||
| import { useState } from "react" | ||||
| import { useTranslation } from "react-i18next" | ||||
| import { deletePromptById, getAllPrompts, savePrompt, updatePrompt } from "@/db" | ||||
| import { | ||||
|   deletePromptById, | ||||
|   getAllPrompts, | ||||
|   savePrompt, | ||||
|   updatePrompt | ||||
| } from "@/db" | ||||
|   getAllCopilotPrompts, | ||||
|   setAllCopilotPrompts | ||||
| } from "@/services/application" | ||||
| import { tagColors } from "@/utils/color" | ||||
| 
 | ||||
| export const PromptBody = () => { | ||||
|   const queryClient = useQueryClient() | ||||
| @ -26,13 +28,25 @@ export const PromptBody = () => { | ||||
|   const [editId, setEditId] = useState("") | ||||
|   const [createForm] = Form.useForm() | ||||
|   const [editForm] = Form.useForm() | ||||
|   const { t } = useTranslation("settings") | ||||
|   const { t } = useTranslation(["settings", "common"]) | ||||
|   const [selectedSegment, setSelectedSegment] = useState<"custom" | "copilot">( | ||||
|     "custom" | ||||
|   ) | ||||
| 
 | ||||
|   const [openCopilotEdit, setOpenCopilotEdit] = useState(false) | ||||
|   const [editCopilotId, setEditCopilotId] = useState("") | ||||
|   const [editCopilotForm] = Form.useForm() | ||||
| 
 | ||||
|   const { data, status } = useQuery({ | ||||
|     queryKey: ["fetchAllPrompts"], | ||||
|     queryFn: getAllPrompts | ||||
|   }) | ||||
| 
 | ||||
|   const { data: copilotData, status: copilotStatus } = useQuery({ | ||||
|     queryKey: ["fetchCopilotPrompts"], | ||||
|     queryFn: getAllCopilotPrompts | ||||
|   }) | ||||
| 
 | ||||
|   const { mutate: deletePrompt } = useMutation({ | ||||
|     mutationFn: deletePromptById, | ||||
|     onSuccess: () => { | ||||
| @ -103,8 +117,38 @@ export const PromptBody = () => { | ||||
|       } | ||||
|     }) | ||||
| 
 | ||||
|   return ( | ||||
|     <div> | ||||
|   const { mutate: updateCopilotPrompt, isPending: isUpdatingCopilotPrompt } = | ||||
|     useMutation({ | ||||
|       mutationFn: async (data: any) => { | ||||
|         return await setAllCopilotPrompts([ | ||||
|           { | ||||
|             key: data.key, | ||||
|             prompt: data.prompt | ||||
|           } | ||||
|         ]) | ||||
|       }, | ||||
|       onSuccess: () => { | ||||
|         queryClient.invalidateQueries({ | ||||
|           queryKey: ["fetchCopilotPrompts"] | ||||
|         }) | ||||
|         setOpenCopilotEdit(false) | ||||
|         editCopilotForm.resetFields() | ||||
|         notification.success({ | ||||
|           message: t("managePrompts.notification.updatedSuccess"), | ||||
|           description: t("managePrompts.notification.updatedSuccessDesc") | ||||
|         }) | ||||
|       }, | ||||
|       onError: (error) => { | ||||
|         notification.error({ | ||||
|           message: t("managePrompts.notification.error"), | ||||
|           description: | ||||
|             error?.message || t("managePrompts.notification.someError") | ||||
|         }) | ||||
|       } | ||||
|     }) | ||||
| 
 | ||||
|   function customPrompts() { | ||||
|     return ( | ||||
|       <div> | ||||
|         <div className="mb-6"> | ||||
|           <div className="-ml-4 -mt-2 flex flex-wrap items-center justify-end sm:flex-nowrap"> | ||||
| @ -127,30 +171,38 @@ export const PromptBody = () => { | ||||
|                 title: t("managePrompts.columns.title"), | ||||
|                 dataIndex: "title", | ||||
|                 key: "title", | ||||
|                 render: (content) => (<span className="line-clamp-1">{content}</span>) | ||||
|                 render: (content) => ( | ||||
|                   <span className="line-clamp-1">{content}</span> | ||||
|                 ) | ||||
|               }, | ||||
|               { | ||||
|                 title: t("managePrompts.columns.prompt"), | ||||
|                 dataIndex: "content", | ||||
|                 key: "content", | ||||
|                 render: (content) => (<span className="line-clamp-1">{content}</span>) | ||||
|                 render: (content) => ( | ||||
|                   <span className="line-clamp-1">{content}</span> | ||||
|                 ) | ||||
|               }, | ||||
|               { | ||||
|                 title: t("managePrompts.columns.type"), | ||||
|                 dataIndex: "is_system", | ||||
|                 key: "is_system", | ||||
|                 render: (is_system) => | ||||
|                 render: (is_system) => ( | ||||
|                   <span className="flex items-center gap-2 text-xs w-32"> | ||||
|                     {is_system ? ( | ||||
|                       <> | ||||
|                         <Computer className="size-4" /> {t("managePrompts.systemPrompt")} | ||||
|                         <Computer className="size-4" />{" "} | ||||
|                         {t("managePrompts.systemPrompt")} | ||||
|                       </> | ||||
|                     ) : ( | ||||
|                       <> | ||||
|                         <Zap className="size-4" /> {t("managePrompts.quickPrompt")} | ||||
|                         <Zap className="size-4" />{" "} | ||||
|                         {t("managePrompts.quickPrompt")} | ||||
|                       </> | ||||
|                     )} | ||||
|                   </span>              }, | ||||
|                   </span> | ||||
|                 ) | ||||
|               }, | ||||
|               { | ||||
|                 title: t("managePrompts.columns.actions"), | ||||
|                 render: (_, record) => ( | ||||
| @ -189,6 +241,90 @@ export const PromptBody = () => { | ||||
|           /> | ||||
|         )} | ||||
|       </div> | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   function copilotPrompts() { | ||||
|     return ( | ||||
|       <div> | ||||
|         {copilotStatus === "pending" && <Skeleton paragraph={{ rows: 8 }} />} | ||||
| 
 | ||||
|         {copilotStatus === "success" && ( | ||||
|           <Table | ||||
|             columns={[ | ||||
|               { | ||||
|                 title: t("managePrompts.columns.title"), | ||||
|                 dataIndex: "key", | ||||
|                 key: "key", | ||||
|                 render: (content) => ( | ||||
|                   <span className="line-clamp-1"> | ||||
|                     <Tag color={tagColors[content || "default"]}> | ||||
|                       {t(`common:copilot.${content}`)} | ||||
|                     </Tag> | ||||
|                   </span> | ||||
|                 ) | ||||
|               }, | ||||
|               { | ||||
|                 title: t("managePrompts.columns.prompt"), | ||||
|                 dataIndex: "prompt", | ||||
|                 key: "prompt", | ||||
|                 render: (content) => ( | ||||
|                   <span className="line-clamp-1">{content}</span> | ||||
|                 ) | ||||
|               }, | ||||
|               { | ||||
|                 render: (_, record) => ( | ||||
|                   <div className="flex gap-4"> | ||||
|                     <Tooltip title={t("managePrompts.tooltip.edit")}> | ||||
|                       <button | ||||
|                         onClick={() => { | ||||
|                           setEditCopilotId(record.key) | ||||
|                           editCopilotForm.setFieldsValue(record) | ||||
|                           setOpenCopilotEdit(true) | ||||
|                         }} | ||||
|                         className="text-gray-500 dark:text-gray-400"> | ||||
|                         <Pen className="size-4" /> | ||||
|                       </button> | ||||
|                     </Tooltip> | ||||
|                   </div> | ||||
|                 ) | ||||
|               } | ||||
|             ]} | ||||
|             bordered | ||||
|             dataSource={copilotData} | ||||
|             rowKey={(record) => record.key} | ||||
|           /> | ||||
|         )} | ||||
|       </div> | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <div> | ||||
|       <div className="flex items-center justify-end mb-6"> | ||||
|         <Segmented | ||||
|           size="large" | ||||
|           options={[ | ||||
|             { | ||||
|               label: t( | ||||
|                 "managePrompts.segmented.custom" | ||||
|               ), | ||||
|               value: "custom" | ||||
|             }, | ||||
|             { | ||||
|               label: t( | ||||
|                 "managePrompts.segmented.copilot" | ||||
|               ), | ||||
|               value: "copilot" | ||||
|             } | ||||
|           ]} | ||||
|           onChange={(value) => { | ||||
|             setSelectedSegment(value as "custom" | "copilot") | ||||
|           }} | ||||
|         /> | ||||
|       </div> | ||||
|       {selectedSegment === "custom" && customPrompts()} | ||||
|       {selectedSegment === "copilot" && copilotPrompts()} | ||||
| 
 | ||||
|       <Modal | ||||
|         title={t("managePrompts.modal.addTitle")} | ||||
| @ -301,6 +437,59 @@ export const PromptBody = () => { | ||||
|           </Form.Item> | ||||
|         </Form> | ||||
|       </Modal> | ||||
| 
 | ||||
|       <Modal | ||||
|         title={t("managePrompts.modal.editTitle")} | ||||
|         open={openCopilotEdit} | ||||
|         onCancel={() => setOpenCopilotEdit(false)} | ||||
|         footer={null}> | ||||
|         <Form | ||||
|           onFinish={(values) => | ||||
|             updateCopilotPrompt({ | ||||
|               key: editCopilotId, | ||||
|               prompt: values.prompt | ||||
|             }) | ||||
|           } | ||||
|           layout="vertical" | ||||
|           form={editCopilotForm}> | ||||
|           <Form.Item | ||||
|             name="prompt" | ||||
|             label={t("managePrompts.form.prompt.label")} | ||||
|             rules={[ | ||||
|               { | ||||
|                 required: true, | ||||
|                 message: t("managePrompts.form.prompt.required") | ||||
|               }, | ||||
|               { | ||||
|                 validator: (_, value) => { | ||||
|                   if (value && value.includes("{text}")) { | ||||
|                     return Promise.resolve() | ||||
|                   } | ||||
|                   return Promise.reject( | ||||
|                     new Error( | ||||
|                       t("managePrompts.form.prompt.missingTextPlaceholder") | ||||
|                     ) | ||||
|                   ) | ||||
|                 } | ||||
|               } | ||||
|             ]}> | ||||
|             <Input.TextArea | ||||
|               placeholder={t("managePrompts.form.prompt.placeholder")} | ||||
|               autoSize={{ minRows: 3, maxRows: 10 }} | ||||
|             /> | ||||
|           </Form.Item> | ||||
| 
 | ||||
|           <Form.Item> | ||||
|             <button | ||||
|               disabled={isUpdatingCopilotPrompt} | ||||
|               className="inline-flex justify-center w-full text-center mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50 "> | ||||
|               {isUpdatingCopilotPrompt | ||||
|                 ? t("managePrompts.form.btnEdit.saving") | ||||
|                 : t("managePrompts.form.btnEdit.save")} | ||||
|             </button> | ||||
|           </Form.Item> | ||||
|         </Form> | ||||
|       </Modal> | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| @ -29,6 +29,8 @@ export const GeneralSettings = () => { | ||||
|     false | ||||
|   ) | ||||
| 
 | ||||
|   const [generateTitle, setGenerateTitle] = useStorage("titleGenEnabled", false) | ||||
| 
 | ||||
|   const [hideCurrentChatModelSettings, setHideCurrentChatModelSettings] = | ||||
|     useStorage("hideCurrentChatModelSettings", false) | ||||
| 
 | ||||
| @ -141,6 +143,19 @@ export const GeneralSettings = () => { | ||||
|         /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div className="flex flex-row justify-between"> | ||||
|         <div className="inline-flex items-center gap-2"> | ||||
|           <span className="text-gray-700   dark:text-neutral-50"> | ||||
|             {t("generalSettings.settings.generateTitle.label")} | ||||
|           </span> | ||||
|         </div> | ||||
| 
 | ||||
|         <Switch | ||||
|           checked={generateTitle} | ||||
|           onChange={(checked) => setGenerateTitle(checked)} | ||||
|         /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div className="flex flex-row justify-between"> | ||||
|         <span className="text-gray-700 dark:text-neutral-50 "> | ||||
|           {t("generalSettings.settings.darkMode.label")} | ||||
|  | ||||
| @ -35,6 +35,7 @@ export const SidePanelBody = () => { | ||||
|           currentMessageIndex={index} | ||||
|           totalMessages={messages.length} | ||||
|           onRengerate={regenerateLastMessage} | ||||
|           message_type={message.messageType} | ||||
|           isProcessing={streaming} | ||||
|           isSearchingInternet={isSearchingInternet} | ||||
|           sources={message.sources} | ||||
|  | ||||
| @ -31,6 +31,7 @@ type Message = { | ||||
|   sources?: string[] | ||||
|   search?: WebSearch | ||||
|   createdAt: number | ||||
|   messageType?: string | ||||
| } | ||||
| 
 | ||||
| type Webshare = { | ||||
| @ -241,7 +242,8 @@ export const saveMessage = async ( | ||||
|   content: string, | ||||
|   images: string[], | ||||
|   source?: any[], | ||||
|   time?: number | ||||
|   time?: number, | ||||
|   message_type?: string | ||||
| ) => { | ||||
|   const id = generateID() | ||||
|   let createdAt = Date.now() | ||||
| @ -256,7 +258,8 @@ export const saveMessage = async ( | ||||
|     content, | ||||
|     images, | ||||
|     createdAt, | ||||
|     sources: source | ||||
|     sources: source, | ||||
|     messageType: message_type | ||||
|   } | ||||
|   const db = new PageAssitDatabase() | ||||
|   await db.addMessage(message) | ||||
|  | ||||
| @ -1,84 +1,10 @@ | ||||
| import { getOllamaURL, isOllamaRunning } from "../services/ollama" | ||||
| import { browser } from "wxt/browser" | ||||
| import { setBadgeBackgroundColor, setBadgeText, setTitle } from "@/utils/action" | ||||
| 
 | ||||
| const progressHuman = (completed: number, total: number) => { | ||||
|   return ((completed / total) * 100).toFixed(0) + "%" | ||||
| } | ||||
| 
 | ||||
| const clearBadge = () => { | ||||
|   setBadgeText({ text: "" }) | ||||
|   setTitle({ title: "" }) | ||||
| } | ||||
| const streamDownload = async (url: string, model: string) => { | ||||
|   url += "/api/pull" | ||||
|   const response = await fetch(url, { | ||||
|     method: "POST", | ||||
|     headers: { | ||||
|       "Content-Type": "application/json" | ||||
|     }, | ||||
|     body: JSON.stringify({ model, stream: true }) | ||||
|   }) | ||||
| 
 | ||||
|   const reader = response.body?.getReader() | ||||
| 
 | ||||
|   const decoder = new TextDecoder() | ||||
| 
 | ||||
|   let isSuccess = true | ||||
|   while (true) { | ||||
|     if (!reader) { | ||||
|       break | ||||
|     } | ||||
|     const { done, value } = await reader.read() | ||||
| 
 | ||||
|     if (done) { | ||||
|       break | ||||
|     } | ||||
| 
 | ||||
|     const text = decoder.decode(value) | ||||
|     try { | ||||
|       const json = JSON.parse(text.trim()) as { | ||||
|         status: string | ||||
|         total?: number | ||||
|         completed?: number | ||||
|       } | ||||
|       if (json.total && json.completed) { | ||||
|         setBadgeText({ | ||||
|           text: progressHuman(json.completed, json.total) | ||||
|         }) | ||||
|         setBadgeBackgroundColor({ color: "#0000FF" }) | ||||
|       } else { | ||||
|         setBadgeText({ text: "🏋️♂️" }) | ||||
|         setBadgeBackgroundColor({ color: "#FFFFFF" }) | ||||
|       } | ||||
| 
 | ||||
|       setTitle({ title: json.status }) | ||||
| 
 | ||||
|       if (json.status === "success") { | ||||
|         isSuccess = true | ||||
|       } | ||||
|     } catch (e) { | ||||
|       console.error(e) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (isSuccess) { | ||||
|     setBadgeText({ text: "✅" }) | ||||
|     setBadgeBackgroundColor({ color: "#00FF00" }) | ||||
|     setTitle({ title: "Model pulled successfully" }) | ||||
|   } else { | ||||
|     setBadgeText({ text: "❌" }) | ||||
|     setBadgeBackgroundColor({ color: "#FF0000" }) | ||||
|     setTitle({ title: "Model pull failed" }) | ||||
|   } | ||||
| 
 | ||||
|   setTimeout(() => { | ||||
|     clearBadge() | ||||
|   }, 5000) | ||||
| } | ||||
| import { clearBadge, streamDownload } from "@/utils/pull-ollama" | ||||
| 
 | ||||
| export default defineBackground({ | ||||
|   main() { | ||||
|     let isCopilotRunning: boolean = false | ||||
|     browser.runtime.onMessage.addListener(async (message) => { | ||||
|       if (message.type === "sidepanel") { | ||||
|         await browser.sidebarAction.open() | ||||
| @ -100,6 +26,15 @@ export default defineBackground({ | ||||
|       } | ||||
|     }) | ||||
| 
 | ||||
|     browser.runtime.onConnect.addListener((port) => { | ||||
|       if (port.name === "pgCopilot") { | ||||
|         isCopilotRunning = true | ||||
|         port.onDisconnect.addListener(() => { | ||||
|           isCopilotRunning = false | ||||
|         }) | ||||
|       } | ||||
|     }) | ||||
| 
 | ||||
|     if (import.meta.env.BROWSER === "chrome") { | ||||
|       chrome.action.onClicked.addListener((tab) => { | ||||
|         chrome.tabs.create({ url: chrome.runtime.getURL("/options.html") }) | ||||
| @ -124,10 +59,41 @@ export default defineBackground({ | ||||
|     browser.contextMenus.create({ | ||||
|       id: contextMenuId["sidePanel"], | ||||
|       title: contextMenuTitle["sidePanel"], | ||||
|       contexts: ["all"] | ||||
|       contexts: ["page", "selection"] | ||||
|     }) | ||||
| 
 | ||||
|     browser.contextMenus.create({ | ||||
|       id: "summarize-pa", | ||||
|       title: "Summarize", | ||||
|       contexts: ["selection"] | ||||
|     }) | ||||
| 
 | ||||
|     browser.contextMenus.create({ | ||||
|       id: "explain-pa", | ||||
|       title: "Explain", | ||||
|       contexts: ["selection"] | ||||
|     }) | ||||
| 
 | ||||
|     browser.contextMenus.create({ | ||||
|       id: "rephrase-pa", | ||||
|       title: "Rephrase", | ||||
|       contexts: ["selection"] | ||||
|     }) | ||||
| 
 | ||||
|     browser.contextMenus.create({ | ||||
|       id: "translate-pg", | ||||
|       title: "Translate", | ||||
|       contexts: ["selection"] | ||||
|     }) | ||||
| 
 | ||||
|     browser.contextMenus.create({ | ||||
|       id: "custom-pg", | ||||
|       title: "Custom", | ||||
|       contexts: ["selection"] | ||||
|     }) | ||||
| 
 | ||||
|     if (import.meta.env.BROWSER === "chrome") { | ||||
|       browser.contextMenus.onClicked.addListener((info, tab) => { | ||||
|       browser.contextMenus.onClicked.addListener(async (info, tab) => { | ||||
|         if (info.menuItemId === "open-side-panel-pa") { | ||||
|           chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
| @ -136,6 +102,68 @@ export default defineBackground({ | ||||
|           browser.tabs.create({ | ||||
|             url: browser.runtime.getURL("/options.html") | ||||
|           }) | ||||
|         } else if (info.menuItemId === "summarize-pa") { | ||||
|           chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
|           }) | ||||
|           // this is a bad method hope somone can fix it :)
 | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               from: "background", | ||||
|               type: "summary", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
| 
 | ||||
|         } else if (info.menuItemId === "rephrase-pa") { | ||||
|           chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
|           }) | ||||
|           setTimeout(async () => { | ||||
| 
 | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "rephrase", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
| 
 | ||||
|         } else if (info.menuItemId === "translate-pg") { | ||||
|           chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
|           }) | ||||
| 
 | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "translate", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } else if (info.menuItemId === "explain-pa") { | ||||
|           chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
|           }) | ||||
| 
 | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "explain", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } else if (info.menuItemId === "custom-pg") { | ||||
|           chrome.sidePanel.open({ | ||||
|             tabId: tab.id! | ||||
|           }) | ||||
| 
 | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "custom", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } | ||||
|       }) | ||||
| 
 | ||||
| @ -166,6 +194,61 @@ export default defineBackground({ | ||||
|           browser.tabs.create({ | ||||
|             url: browser.runtime.getURL("/options.html") | ||||
|           }) | ||||
|         } else if (info.menuItemId === "summarize-pa") { | ||||
|           if (!isCopilotRunning) { | ||||
|             browser.sidebarAction.toggle() | ||||
|           } | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               from: "background", | ||||
|               type: "summary", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } else if (info.menuItemId === "rephrase-pa") { | ||||
|           if (!isCopilotRunning) { | ||||
|             browser.sidebarAction.toggle() | ||||
|           } | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "rephrase", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } else if (info.menuItemId === "translate-pg") { | ||||
|           if (!isCopilotRunning) { | ||||
|             browser.sidebarAction.toggle() | ||||
|           } | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "translate", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } else if (info.menuItemId === "explain-pa") { | ||||
|           if (!isCopilotRunning) { | ||||
|             browser.sidebarAction.toggle() | ||||
|           } | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "explain", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } else if (info.menuItemId === "custom-pg") { | ||||
|           if (!isCopilotRunning) { | ||||
|             browser.sidebarAction.toggle() | ||||
|           } | ||||
|           setTimeout(async () => { | ||||
|             await browser.runtime.sendMessage({ | ||||
|               type: "custom", | ||||
|               from: "background", | ||||
|               text: info.selectionText | ||||
|             }) | ||||
|           }, isCopilotRunning ? 0 : 5000) | ||||
|         } | ||||
|       }) | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { QueryClient, QueryClientProvider } from "@tanstack/react-query" | ||||
| import { MemoryRouter } from "react-router-dom" | ||||
| import { useEffect, useState } from "react" | ||||
| const queryClient = new QueryClient() | ||||
| import { ConfigProvider, Empty, theme } from "antd" | ||||
| import { StyleProvider } from "@ant-design/cssinjs" | ||||
| @ -12,6 +13,16 @@ import { PageAssistProvider } from "@/components/Common/PageAssistProvider" | ||||
| function IndexOption() { | ||||
|   const { mode } = useDarkMode() | ||||
|   const { t, i18n } = useTranslation() | ||||
|   const [direction, setDirection] = useState<"ltr" | "rtl">("ltr") | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (i18n.resolvedLanguage) { | ||||
|       document.documentElement.lang = i18n.resolvedLanguage | ||||
|       document.documentElement.dir = i18n.dir(i18n.resolvedLanguage) | ||||
|       setDirection(i18n.dir(i18n.resolvedLanguage)) | ||||
|     } | ||||
|   }, [i18n, i18n.resolvedLanguage]) | ||||
| 
 | ||||
|   return ( | ||||
|     <MemoryRouter> | ||||
|       <ConfigProvider | ||||
| @ -29,7 +40,8 @@ function IndexOption() { | ||||
|             }} | ||||
|             description={t("common:noData")} | ||||
|           /> | ||||
|         )}> | ||||
|         )} | ||||
|         direction={direction}> | ||||
|         <StyleProvider hashPriority="high"> | ||||
|           <QueryClientProvider client={queryClient}> | ||||
|             <PageAssistProvider> | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { QueryClient, QueryClientProvider } from "@tanstack/react-query" | ||||
| import { MemoryRouter } from "react-router-dom" | ||||
| import { useEffect } from "react" | ||||
| import { SidepanelRouting } from "~/routes" | ||||
| const queryClient = new QueryClient() | ||||
| import { ConfigProvider, Empty, theme } from "antd" | ||||
| @ -13,6 +14,13 @@ function IndexSidepanel() { | ||||
|   const { mode } = useDarkMode() | ||||
|   const { t, i18n } = useTranslation() | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (i18n.resolvedLanguage) { | ||||
|       document.documentElement.lang = i18n.resolvedLanguage; | ||||
|       document.documentElement.dir = i18n.dir(i18n.resolvedLanguage); | ||||
|     } | ||||
|   }, [i18n, i18n.resolvedLanguage]); | ||||
| 
 | ||||
|   return ( | ||||
|     <MemoryRouter> | ||||
|       <ConfigProvider | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { saveHistory, saveMessage } from "@/db" | ||||
| import { setLastUsedChatModel } from "@/services/model-settings" | ||||
| import { generateTitle } from "@/services/title" | ||||
| import { ChatHistory } from "@/store/option" | ||||
| 
 | ||||
| export const saveMessageOnError = async ({ | ||||
| @ -13,7 +14,8 @@ export const saveMessageOnError = async ({ | ||||
|   selectedModel, | ||||
|   setHistoryId, | ||||
|   isRegenerating, | ||||
|   message_source = "web-ui" | ||||
|   message_source = "web-ui", | ||||
|   message_type | ||||
| }: { | ||||
|   e: any | ||||
|   setHistory: (history: ChatHistory) => void | ||||
| @ -26,6 +28,7 @@ export const saveMessageOnError = async ({ | ||||
|   setHistoryId: (historyId: string) => void | ||||
|   isRegenerating: boolean | ||||
|   message_source?: "copilot" | "web-ui" | ||||
|   message_type?: string | ||||
| }) => { | ||||
|   if ( | ||||
|     e?.name === "AbortError" || | ||||
| @ -55,7 +58,8 @@ export const saveMessageOnError = async ({ | ||||
|           userMessage, | ||||
|           [image], | ||||
|           [], | ||||
|           1 | ||||
|           1, | ||||
|           message_type | ||||
|         ) | ||||
|       } | ||||
|       await saveMessage( | ||||
| @ -65,11 +69,13 @@ export const saveMessageOnError = async ({ | ||||
|         botMessage, | ||||
|         [], | ||||
|         [], | ||||
|         2 | ||||
|         2, | ||||
|         message_type | ||||
|       ) | ||||
|       await setLastUsedChatModel(historyId, selectedModel) | ||||
|     } else { | ||||
|       const newHistoryId = await saveHistory(userMessage, false, message_source) | ||||
|       const title = await generateTitle(selectedModel, userMessage, userMessage) | ||||
|       const newHistoryId = await saveHistory(title, false, message_source) | ||||
|       if (!isRegenerating) { | ||||
|         await saveMessage( | ||||
|           newHistoryId.id, | ||||
| @ -78,7 +84,8 @@ export const saveMessageOnError = async ({ | ||||
|           userMessage, | ||||
|           [image], | ||||
|           [], | ||||
|           1 | ||||
|           1, | ||||
|           message_type | ||||
|         ) | ||||
|       } | ||||
|       await saveMessage( | ||||
| @ -88,7 +95,8 @@ export const saveMessageOnError = async ({ | ||||
|         botMessage, | ||||
|         [], | ||||
|         [], | ||||
|         2 | ||||
|         2, | ||||
|         message_type | ||||
|       ) | ||||
|       setHistoryId(newHistoryId.id) | ||||
|       await setLastUsedChatModel(newHistoryId.id, selectedModel) | ||||
| @ -109,7 +117,8 @@ export const saveMessageOnSuccess = async ({ | ||||
|   image, | ||||
|   fullText, | ||||
|   source, | ||||
|   message_source = "web-ui" | ||||
|   message_source = "web-ui", | ||||
|   message_type | ||||
| }: { | ||||
|   historyId: string | null | ||||
|   setHistoryId: (historyId: string) => void | ||||
| @ -119,7 +128,8 @@ export const saveMessageOnSuccess = async ({ | ||||
|   image: string | ||||
|   fullText: string | ||||
|   source: any[] | ||||
|   message_source?: "copilot" | "web-ui" | ||||
|   message_source?: "copilot" | "web-ui", | ||||
|   message_type?: string | ||||
| }) => { | ||||
|   if (historyId) { | ||||
|     if (!isRegenerate) { | ||||
| @ -130,7 +140,8 @@ export const saveMessageOnSuccess = async ({ | ||||
|         message, | ||||
|         [image], | ||||
|         [], | ||||
|         1 | ||||
|         1, | ||||
|         message_type | ||||
|       ) | ||||
|     } | ||||
|     await saveMessage( | ||||
| @ -140,11 +151,13 @@ export const saveMessageOnSuccess = async ({ | ||||
|       fullText, | ||||
|       [], | ||||
|       source, | ||||
|       2 | ||||
|       2, | ||||
|       message_type | ||||
|     ) | ||||
|     await setLastUsedChatModel(historyId, selectedModel!) | ||||
|   } else { | ||||
|     const newHistoryId = await saveHistory(message, false, message_source) | ||||
|     const title = await generateTitle(selectedModel, message, message) | ||||
|     const newHistoryId = await saveHistory(title, false, message_source) | ||||
|     await saveMessage( | ||||
|       newHistoryId.id, | ||||
|       selectedModel, | ||||
| @ -152,7 +165,8 @@ export const saveMessageOnSuccess = async ({ | ||||
|       message, | ||||
|       [image], | ||||
|       [], | ||||
|       1 | ||||
|       1, | ||||
|       message_type | ||||
|     ) | ||||
|     await saveMessage( | ||||
|       newHistoryId.id, | ||||
| @ -161,7 +175,8 @@ export const saveMessageOnSuccess = async ({ | ||||
|       fullText, | ||||
|       [], | ||||
|       source, | ||||
|       2 | ||||
|       2, | ||||
|       message_type | ||||
|     ) | ||||
|     setHistoryId(newHistoryId.id) | ||||
|     await setLastUsedChatModel(newHistoryId.id, selectedModel!) | ||||
|  | ||||
							
								
								
									
										29
									
								
								src/hooks/useBackgroundMessage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/hooks/useBackgroundMessage.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| import { useState, useEffect } from "react" | ||||
| 
 | ||||
| interface Message { | ||||
|   from: string | ||||
|   type: string | ||||
|   text: string | ||||
| } | ||||
| 
 | ||||
| function useBackgroundMessage() { | ||||
|   const [message, setMessage] = useState<Message | null>(null) | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const messageListener = (request: Message) => { | ||||
|       if (request.from === "background") { | ||||
|         setMessage(request) | ||||
|       } | ||||
|     } | ||||
|     browser.runtime.connect({ name: 'pgCopilot' }) | ||||
|     browser.runtime.onMessage.addListener(messageListener) | ||||
| 
 | ||||
|     return () => { | ||||
|       browser.runtime.onMessage.removeListener(messageListener) | ||||
|     } | ||||
|   }, []) | ||||
| 
 | ||||
|   return message | ||||
| } | ||||
| 
 | ||||
| export default useBackgroundMessage | ||||
| @ -31,6 +31,7 @@ import { useStoreChatModelSettings } from "@/store/model" | ||||
| import { getAllDefaultModelSettings } from "@/services/model-settings" | ||||
| import { getSystemPromptForWeb } from "@/web/web" | ||||
| import { pageAssistModel } from "@/models" | ||||
| import { getPrompt } from "@/services/application" | ||||
| 
 | ||||
| export const useMessage = () => { | ||||
|   const { | ||||
| @ -51,8 +52,10 @@ export const useMessage = () => { | ||||
|     isSearchingInternet | ||||
|   } = useStoreMessageOption() | ||||
| 
 | ||||
| 
 | ||||
|   const [chatWithWebsiteEmbedding] = useStorage("chatWithWebsiteEmbedding", true) | ||||
|   const [chatWithWebsiteEmbedding] = useStorage( | ||||
|     "chatWithWebsiteEmbedding", | ||||
|     true | ||||
|   ) | ||||
|   const [maxWebsiteContext] = useStorage("maxWebsiteContext", 4028) | ||||
| 
 | ||||
|   const { | ||||
| @ -857,13 +860,206 @@ export const useMessage = () => { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   const presetChatMode = async ( | ||||
|     message: string, | ||||
|     image: string, | ||||
|     isRegenerate: boolean, | ||||
|     messages: Message[], | ||||
|     history: ChatHistory, | ||||
|     signal: AbortSignal, | ||||
|     messageType: string | ||||
|   ) => { | ||||
|     setStreaming(true) | ||||
|     const url = await getOllamaURL() | ||||
|     const userDefaultModelSettings = await getAllDefaultModelSettings() | ||||
| 
 | ||||
|     if (image.length > 0) { | ||||
|       image = `data:image/jpeg;base64,${image.split(",")[1]}` | ||||
|     } | ||||
| 
 | ||||
|     const ollama = await pageAssistModel({ | ||||
|       model: selectedModel!, | ||||
|       baseUrl: cleanUrl(url), | ||||
|       keepAlive: | ||||
|         currentChatModelSettings?.keepAlive ?? | ||||
|         userDefaultModelSettings?.keepAlive, | ||||
|       temperature: | ||||
|         currentChatModelSettings?.temperature ?? | ||||
|         userDefaultModelSettings?.temperature, | ||||
|       topK: currentChatModelSettings?.topK ?? userDefaultModelSettings?.topK, | ||||
|       topP: currentChatModelSettings?.topP ?? userDefaultModelSettings?.topP, | ||||
|       numCtx: | ||||
|         currentChatModelSettings?.numCtx ?? userDefaultModelSettings?.numCtx, | ||||
|       seed: currentChatModelSettings?.seed | ||||
|     }) | ||||
| 
 | ||||
|     let newMessage: Message[] = [] | ||||
|     let generateMessageId = generateID() | ||||
| 
 | ||||
|     if (!isRegenerate) { | ||||
|       newMessage = [ | ||||
|         ...messages, | ||||
|         { | ||||
|           isBot: false, | ||||
|           name: "You", | ||||
|           message, | ||||
|           sources: [], | ||||
|           images: [image], | ||||
|           messageType: messageType | ||||
|         }, | ||||
|         { | ||||
|           isBot: true, | ||||
|           name: selectedModel, | ||||
|           message: "▋", | ||||
|           sources: [], | ||||
|           id: generateMessageId | ||||
|         } | ||||
|       ] | ||||
|     } else { | ||||
|       newMessage = [ | ||||
|         ...messages, | ||||
|         { | ||||
|           isBot: true, | ||||
|           name: selectedModel, | ||||
|           message: "▋", | ||||
|           sources: [], | ||||
|           id: generateMessageId | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|     setMessages(newMessage) | ||||
|     let fullText = "" | ||||
|     let contentToSave = "" | ||||
| 
 | ||||
|     try { | ||||
| 
 | ||||
|        | ||||
|       const prompt = await getPrompt(messageType) | ||||
|       let humanMessage = new HumanMessage({ | ||||
|         content: [ | ||||
|           { | ||||
|             text: prompt.replace("{text}", message), | ||||
|             type: "text" | ||||
|           } | ||||
|         ] | ||||
|       }) | ||||
|       if (image.length > 0) { | ||||
|         humanMessage = new HumanMessage({ | ||||
|           content: [ | ||||
|             { | ||||
|               text: prompt.replace("{text}", message), | ||||
|               type: "text" | ||||
|             }, | ||||
|             { | ||||
|               image_url: image, | ||||
|               type: "image_url" | ||||
|             } | ||||
|           ] | ||||
|         }) | ||||
|       } | ||||
| 
 | ||||
|       const chunks = await ollama.stream([humanMessage], { | ||||
|         signal: signal | ||||
|       }) | ||||
|       let count = 0 | ||||
|       for await (const chunk of chunks) { | ||||
|         contentToSave += chunk.content | ||||
|         fullText += chunk.content | ||||
|         if (count === 0) { | ||||
|           setIsProcessing(true) | ||||
|         } | ||||
|         setMessages((prev) => { | ||||
|           return prev.map((message) => { | ||||
|             if (message.id === generateMessageId) { | ||||
|               return { | ||||
|                 ...message, | ||||
|                 message: fullText + "▋" | ||||
|               } | ||||
|             } | ||||
|             return message | ||||
|           }) | ||||
|         }) | ||||
|         count++ | ||||
|       } | ||||
| 
 | ||||
|       setMessages((prev) => { | ||||
|         return prev.map((message) => { | ||||
|           if (message.id === generateMessageId) { | ||||
|             return { | ||||
|               ...message, | ||||
|               message: fullText | ||||
|             } | ||||
|           } | ||||
|           return message | ||||
|         }) | ||||
|       }) | ||||
| 
 | ||||
|       setHistory([ | ||||
|         ...history, | ||||
|         { | ||||
|           role: "user", | ||||
|           content: message, | ||||
|           image, | ||||
|           messageType | ||||
|         }, | ||||
|         { | ||||
|           role: "assistant", | ||||
|           content: fullText | ||||
|         } | ||||
|       ]) | ||||
| 
 | ||||
|       await saveMessageOnSuccess({ | ||||
|         historyId, | ||||
|         setHistoryId, | ||||
|         isRegenerate, | ||||
|         selectedModel: selectedModel, | ||||
|         message, | ||||
|         image, | ||||
|         fullText, | ||||
|         source: [], | ||||
|         message_source: "copilot", | ||||
|         message_type: messageType | ||||
|       }) | ||||
| 
 | ||||
|       setIsProcessing(false) | ||||
|       setStreaming(false) | ||||
|     } catch (e) { | ||||
|       const errorSave = await saveMessageOnError({ | ||||
|         e, | ||||
|         botMessage: fullText, | ||||
|         history, | ||||
|         historyId, | ||||
|         image, | ||||
|         selectedModel, | ||||
|         setHistory, | ||||
|         setHistoryId, | ||||
|         userMessage: message, | ||||
|         isRegenerating: isRegenerate, | ||||
|         message_source: "copilot", | ||||
|         message_type: messageType | ||||
|       }) | ||||
| 
 | ||||
|       if (!errorSave) { | ||||
|         notification.error({ | ||||
|           message: t("error"), | ||||
|           description: e?.message || t("somethingWentWrong") | ||||
|         }) | ||||
|       } | ||||
|       setIsProcessing(false) | ||||
|       setStreaming(false) | ||||
|     } finally { | ||||
|       setAbortController(null) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   const onSubmit = async ({ | ||||
|     message, | ||||
|     image, | ||||
|     isRegenerate, | ||||
|     controller, | ||||
|     memory, | ||||
|     messages: chatHistory | ||||
|     messages: chatHistory, | ||||
|     messageType | ||||
|   }: { | ||||
|     message: string | ||||
|     image: string | ||||
| @ -871,6 +1067,7 @@ export const useMessage = () => { | ||||
|     messages?: Message[] | ||||
|     memory?: ChatHistory | ||||
|     controller?: AbortController | ||||
|     messageType?: string | ||||
|   }) => { | ||||
|     let signal: AbortSignal | ||||
|     if (!controller) { | ||||
| @ -882,39 +1079,52 @@ export const useMessage = () => { | ||||
|       signal = controller.signal | ||||
|     } | ||||
| 
 | ||||
|     if (chatMode === "normal") { | ||||
|       if (webSearch) { | ||||
|         await searchChatMode( | ||||
|           message, | ||||
|           image, | ||||
|           isRegenerate || false, | ||||
|           messages, | ||||
|           memory || history, | ||||
|           signal | ||||
|         ) | ||||
|       } else { | ||||
|         await normalChatMode( | ||||
|           message, | ||||
|           image, | ||||
|           isRegenerate, | ||||
|           chatHistory || messages, | ||||
|           memory || history, | ||||
|           signal | ||||
|         ) | ||||
|       } | ||||
|     } else { | ||||
|       const newEmbeddingController = new AbortController() | ||||
|       let embeddingSignal = newEmbeddingController.signal | ||||
|       setEmbeddingController(newEmbeddingController) | ||||
|       await chatWithWebsiteMode( | ||||
|     // this means that the user is trying to send something from a selected text on the web
 | ||||
|     if (messageType) { | ||||
|       await presetChatMode( | ||||
|         message, | ||||
|         image, | ||||
|         isRegenerate, | ||||
|         chatHistory || messages, | ||||
|         memory || history, | ||||
|         signal, | ||||
|         embeddingSignal | ||||
|         messageType | ||||
|       ) | ||||
|     } else { | ||||
|       if (chatMode === "normal") { | ||||
|         if (webSearch) { | ||||
|           await searchChatMode( | ||||
|             message, | ||||
|             image, | ||||
|             isRegenerate || false, | ||||
|             messages, | ||||
|             memory || history, | ||||
|             signal | ||||
|           ) | ||||
|         } else { | ||||
|           await normalChatMode( | ||||
|             message, | ||||
|             image, | ||||
|             isRegenerate, | ||||
|             chatHistory || messages, | ||||
|             memory || history, | ||||
|             signal | ||||
|           ) | ||||
|         } | ||||
|       } else { | ||||
|         const newEmbeddingController = new AbortController() | ||||
|         let embeddingSignal = newEmbeddingController.signal | ||||
|         setEmbeddingController(newEmbeddingController) | ||||
|         await chatWithWebsiteMode( | ||||
|           message, | ||||
|           image, | ||||
|           isRegenerate, | ||||
|           chatHistory || messages, | ||||
|           memory || history, | ||||
|           signal, | ||||
|           embeddingSignal | ||||
|         ) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -982,7 +1192,8 @@ export const useMessage = () => { | ||||
|           image: lastMessage.image || "", | ||||
|           isRegenerate: true, | ||||
|           memory: newHistory, | ||||
|           controller: newController | ||||
|           controller: newController, | ||||
|           messageType: lastMessage.messageType | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @ -9,6 +9,7 @@ import { zh } from "./lang/zh"; | ||||
| import { ja } from "./lang/ja"; | ||||
| import { it } from "./lang/it"; | ||||
| import { es } from "./lang/es"; | ||||
| import { fa } from "./lang/fa"; | ||||
| import LanguageDetector from 'i18next-browser-languagedetector'; | ||||
| 
 | ||||
| i18n | ||||
| @ -27,7 +28,9 @@ i18n | ||||
|             "ru-RU": ru, | ||||
|             zh: zh, | ||||
|             ja: ja, | ||||
|             "ja-JP": ja | ||||
|             "ja-JP": ja, | ||||
|             fa: fa, | ||||
|             "fa-IR": fa | ||||
|         }, | ||||
|         fallbackLng: "en", | ||||
|         lng: localStorage.getItem("i18nextLng") || "en", | ||||
|  | ||||
							
								
								
									
										17
									
								
								src/i18n/lang/fa.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/i18n/lang/fa.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import option from "@/assets/locale/fa/option.json" | ||||
| import playground from "@/assets/locale/fa/playground.json" | ||||
| import common from "@/assets/locale/fa/common.json" | ||||
| import sidepanel from "@/assets/locale/fa/sidepanel.json" | ||||
| import settings from "@/assets/locale/fa/settings.json" | ||||
| import knowledge from "@/assets/locale/fa/knowledge.json" | ||||
| import chrome from "@/assets/locale/fa/chrome.json" | ||||
| 
 | ||||
| export const fa = { | ||||
|     option, | ||||
|     playground, | ||||
|     common, | ||||
|     sidepanel, | ||||
|     settings, | ||||
|     knowledge, | ||||
|     chrome | ||||
| } | ||||
| @ -35,5 +35,9 @@ export const supportLanguage = [ | ||||
|     { | ||||
|         label: "日本語", | ||||
|         value: "ja-JP" | ||||
|     }, | ||||
|     { | ||||
|         label: "فارسی", | ||||
|         value: "fa" | ||||
|     } | ||||
| ] | ||||
|  | ||||
| @ -13,12 +13,12 @@ export const pageAssistModel = async ({ | ||||
| }: { | ||||
|   model: string | ||||
|   baseUrl: string | ||||
|   keepAlive: string | ||||
|   temperature: number | ||||
|   topK: number | ||||
|   topP: number | ||||
|   numCtx: number | ||||
|   seed: number | ||||
|   keepAlive?: string | ||||
|   temperature?: number | ||||
|   topK?: number | ||||
|   topP?: number | ||||
|   numCtx?: number | ||||
|   seed?: number | ||||
| }) => { | ||||
|   switch (model) { | ||||
|     case "chrome::gemini-nano::page-assist": | ||||
|  | ||||
							
								
								
									
										14
									
								
								src/public/_locales/fa/messages.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/public/_locales/fa/messages.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| { | ||||
|     "extName": { | ||||
|         "message": "دستیار صفحه - یک رابط کاربری وب برای مدل های هوش مصنوعی لوکال" | ||||
|     }, | ||||
|     "extDescription": { | ||||
|         "message": "از مدل های هوش مصنوعی لوکال خود برای دریافت کمک در هنگام مرور وب استفاده کنید." | ||||
|     }, | ||||
|     "openSidePanelToChat": { | ||||
|         "message": "باز کردن کوپایلت برای گفتگو" | ||||
|     }, | ||||
|     "openOptionToChat": { | ||||
|         "message": "باز کردن رابط کاربری وب برای گفتگو" | ||||
|     } | ||||
| } | ||||
| @ -3,8 +3,11 @@ import { | ||||
|   formatToMessage, | ||||
|   getRecentChatFromCopilot | ||||
| } from "@/db" | ||||
| import useBackgroundMessage from "@/hooks/useBackgroundMessage" | ||||
| import { copilotResumeLastChat } from "@/services/app" | ||||
| import { notification } from "antd" | ||||
| import React from "react" | ||||
| import { useTranslation } from "react-i18next" | ||||
| import { SidePanelBody } from "~/components/Sidepanel/Chat/body" | ||||
| import { SidepanelForm } from "~/components/Sidepanel/Chat/form" | ||||
| import { SidepanelHeader } from "~/components/Sidepanel/Chat/header" | ||||
| @ -13,17 +16,27 @@ import { useMessage } from "~/hooks/useMessage" | ||||
| const SidepanelChat = () => { | ||||
|   const drop = React.useRef<HTMLDivElement>(null) | ||||
|   const [dropedFile, setDropedFile] = React.useState<File | undefined>() | ||||
|   const { t } = useTranslation(["playground"]) | ||||
|   const [dropState, setDropState] = React.useState< | ||||
|     "idle" | "dragging" | "error" | ||||
|   >("idle") | ||||
|   const { chatMode, messages, setHistory, setHistoryId, setMessages } = | ||||
|     useMessage() | ||||
|   const { | ||||
|     chatMode, | ||||
|     streaming, | ||||
|     onSubmit, | ||||
|     messages, | ||||
|     setHistory, | ||||
|     setHistoryId, | ||||
|     setMessages, | ||||
|     selectedModel | ||||
|   } = useMessage() | ||||
| 
 | ||||
|   const bgMsg = useBackgroundMessage() | ||||
| 
 | ||||
|   const setRecentMessagesOnLoad = async () => { | ||||
| 
 | ||||
|     const isEnabled = await copilotResumeLastChat(); | ||||
|     const isEnabled = await copilotResumeLastChat() | ||||
|     if (!isEnabled) { | ||||
|       return; | ||||
|       return | ||||
|     } | ||||
|     if (messages.length === 0) { | ||||
|       const recentChat = await getRecentChatFromCopilot() | ||||
| @ -92,11 +105,26 @@ const SidepanelChat = () => { | ||||
|     } | ||||
|   }, []) | ||||
| 
 | ||||
| 
 | ||||
|   React.useEffect(() => { | ||||
|     setRecentMessagesOnLoad() | ||||
|   }, []) | ||||
| 
 | ||||
|   React.useEffect(() => { | ||||
|     if (bgMsg && !streaming) { | ||||
|       if (selectedModel) { | ||||
|         onSubmit({ | ||||
|           message: bgMsg.text, | ||||
|           messageType: bgMsg.type, | ||||
|           image: "" | ||||
|         }) | ||||
|       } else { | ||||
|         notification.error({ | ||||
|           message: t("formError.noModel") | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   }, [bgMsg]) | ||||
| 
 | ||||
|   return ( | ||||
|     <div | ||||
|       ref={drop} | ||||
|  | ||||
							
								
								
									
										162
									
								
								src/services/application.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/services/application.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,162 @@ | ||||
| import { Storage } from "@plasmohq/storage" | ||||
| const storage = new Storage() | ||||
| 
 | ||||
| const DEFAULT_SUMMARY_PROMPT = `Provide a concise summary of the following text, capturing its main ideas and key points:
 | ||||
| 
 | ||||
| Text: | ||||
| --------- | ||||
| {text} | ||||
| --------- | ||||
| 
 | ||||
| Summarize the text in no more than 3-4 sentences. | ||||
| 
 | ||||
| Response:` | ||||
| 
 | ||||
| const DEFAULT_REPHRASE_PROMPT = `Rewrite the following text in a different way, maintaining its original meaning but using alternative vocabulary and sentence structures:
 | ||||
| 
 | ||||
| Text: | ||||
| --------- | ||||
| {text} | ||||
| --------- | ||||
| 
 | ||||
| Ensure that your rephrased version conveys the same information and intent as the original. | ||||
| 
 | ||||
| Response:` | ||||
| 
 | ||||
| const DEFAULT_TRANSLATE_PROMPT = `Translate the following text from its original language into "english". Maintain the tone and style of the original text as much as possible:
 | ||||
| 
 | ||||
| Text: | ||||
| --------- | ||||
| {text} | ||||
| --------- | ||||
| 
 | ||||
| Response:` | ||||
| 
 | ||||
| const DEFAULT_EXPLAIN_PROMPT = `Provide a detailed explanation of the following text, breaking down its key concepts, implications, and context:
 | ||||
| 
 | ||||
| Text: | ||||
| --------- | ||||
| {text} | ||||
| --------- | ||||
| 
 | ||||
| Your explanation should: | ||||
| 
 | ||||
| Clarify any complex terms or ideas | ||||
| Provide relevant background information | ||||
| Discuss the significance or implications of the content | ||||
| Address any potential questions a reader might have | ||||
| Use examples or analogies to illustrate points when appropriate | ||||
| 
 | ||||
| Aim for a comprehensive explanation that would help someone with little prior knowledge fully understand the text. | ||||
| 
 | ||||
| Response:` | ||||
| 
 | ||||
| const DEFAULT_CUSTOM_PROMPT = `{text}` | ||||
| 
 | ||||
| export const getSummaryPrompt = async () => { | ||||
|     return (await storage.get("copilotSummaryPrompt")) || DEFAULT_SUMMARY_PROMPT | ||||
| } | ||||
| 
 | ||||
| export const setSummaryPrompt = async (prompt: string) => { | ||||
|     await storage.set("copilotSummaryPrompt", prompt) | ||||
| } | ||||
| 
 | ||||
| export const getRephrasePrompt = async () => { | ||||
|     return (await storage.get("copilotRephrasePrompt")) || DEFAULT_REPHRASE_PROMPT | ||||
| } | ||||
| 
 | ||||
| export const setRephrasePrompt = async (prompt: string) => { | ||||
|     await storage.set("copilotRephrasePrompt", prompt) | ||||
| } | ||||
| 
 | ||||
| export const getTranslatePrompt = async () => { | ||||
|     return ( | ||||
|         (await storage.get("copilotTranslatePrompt")) || DEFAULT_TRANSLATE_PROMPT | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| export const setTranslatePrompt = async (prompt: string) => { | ||||
|     await storage.set("copilotTranslatePrompt", prompt) | ||||
| } | ||||
| 
 | ||||
| export const getExplainPrompt = async () => { | ||||
|     return (await storage.get("copilotExplainPrompt")) || DEFAULT_EXPLAIN_PROMPT | ||||
| } | ||||
| 
 | ||||
| export const setExplainPrompt = async (prompt: string) => { | ||||
|     await storage.set("copilotExplainPrompt", prompt) | ||||
| } | ||||
| 
 | ||||
| export const getCustomPrompt = async () => { | ||||
|     return (await storage.get("copilotCustomPrompt")) || DEFAULT_CUSTOM_PROMPT | ||||
| } | ||||
| 
 | ||||
| export const setCustomPrompt = async (prompt: string) => { | ||||
|     await storage.set("copilotCustomPrompt", prompt) | ||||
| } | ||||
| 
 | ||||
| export const getAllCopilotPrompts = async () => { | ||||
|     const [ | ||||
|         summaryPrompt, | ||||
|         rephrasePrompt, | ||||
|         translatePrompt, | ||||
|         explainPrompt, | ||||
|         customPrompt | ||||
|     ] = await Promise.all([ | ||||
|         getSummaryPrompt(), | ||||
|         getRephrasePrompt(), | ||||
|         getTranslatePrompt(), | ||||
|         getExplainPrompt(), | ||||
|         getCustomPrompt() | ||||
|     ]) | ||||
| 
 | ||||
|     return [ | ||||
|         { key: "summary", prompt: summaryPrompt }, | ||||
|         { key: "rephrase", prompt: rephrasePrompt }, | ||||
|         { key: "translate", prompt: translatePrompt }, | ||||
|         { key: "explain", prompt: explainPrompt }, | ||||
|         { key: "custom", prompt: customPrompt } | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| export const setAllCopilotPrompts = async ( | ||||
|     prompts: { key: string; prompt: string }[] | ||||
| ) => { | ||||
|     for (const { key, prompt } of prompts) { | ||||
|         switch (key) { | ||||
|             case "summary": | ||||
|                 await setSummaryPrompt(prompt) | ||||
|                 break | ||||
|             case "rephrase": | ||||
|                 await setRephrasePrompt(prompt) | ||||
|                 break | ||||
|             case "translate": | ||||
|                 await setTranslatePrompt(prompt) | ||||
|                 break | ||||
|             case "explain": | ||||
|                 await setExplainPrompt(prompt) | ||||
|                 break | ||||
|             case "custom": | ||||
|                 await setCustomPrompt(prompt) | ||||
|                 break | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const getPrompt = async (key: string) => { | ||||
|     switch (key) { | ||||
|         case "summary": | ||||
|             return await getSummaryPrompt() | ||||
|         case "rephrase": | ||||
|             return await getRephrasePrompt() | ||||
|         case "translate": | ||||
|             return await getTranslatePrompt() | ||||
|         case "explain": | ||||
|             return await getExplainPrompt() | ||||
|         case "custom": | ||||
|             return await getCustomPrompt() | ||||
|         default: | ||||
|             return "" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										72
									
								
								src/services/title.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/services/title.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| import { pageAssistModel } from "@/models" | ||||
| import { Storage } from "@plasmohq/storage" | ||||
| import { getOllamaURL } from "./ollama" | ||||
| import { cleanUrl } from "@/libs/clean-url" | ||||
| import { HumanMessage } from "langchain/schema" | ||||
| const storage = new Storage() | ||||
| 
 | ||||
| // this prompt is copied from the OpenWebUI codebase
 | ||||
| export const DEFAULT_TITLE_GEN_PROMPT = `Here is the query:
 | ||||
| 
 | ||||
| -------------- | ||||
| 
 | ||||
| {{query}} | ||||
| 
 | ||||
| -------------- | ||||
| 
 | ||||
| Create a concise, 3-5 word phrase as a title for the previous query. Avoid quotation marks or special formatting. RESPOND ONLY WITH THE TITLE TEXT. ANSWER USING THE SAME LANGUAGE AS THE QUERY. | ||||
| 
 | ||||
| 
 | ||||
| Examples of titles: | ||||
| 
 | ||||
| Stellar Achievement Celebration | ||||
| Family Bonding Activities | ||||
| 🇫🇷 Voyage à Paris | ||||
| 🍜 Receta de Ramen Casero | ||||
| Shakespeare Analyse Literarische | ||||
| 日本の春祭り体験 | ||||
| Древнегреческая Философия Обзор | ||||
| 
 | ||||
| Response:` | ||||
| 
 | ||||
| 
 | ||||
| export const isTitleGenEnabled = async () => { | ||||
|     const enabled = await storage.get<boolean | undefined>("titleGenEnabled") | ||||
|     return enabled ?? false | ||||
| } | ||||
| 
 | ||||
| export const setTitleGenEnabled = async (enabled: boolean) => { | ||||
|     await storage.set("titleGenEnabled", enabled) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export const generateTitle = async (model: string, query: string, fallBackTitle: string) => { | ||||
| 
 | ||||
|     const isEnabled = await isTitleGenEnabled() | ||||
| 
 | ||||
|     if (!isEnabled) { | ||||
|         return fallBackTitle | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         const url = await getOllamaURL() | ||||
| 
 | ||||
|         const titleModel = await pageAssistModel({ | ||||
|             baseUrl: cleanUrl(url), | ||||
|             model | ||||
|         }) | ||||
| 
 | ||||
|         const prompt = DEFAULT_TITLE_GEN_PROMPT.replace("{{query}}", query) | ||||
| 
 | ||||
|         const title = await titleModel.invoke([ | ||||
|             new HumanMessage({ | ||||
|                 content: prompt | ||||
|             }) | ||||
|         ]) | ||||
| 
 | ||||
|         return title.content.toString() | ||||
|     } catch (error) { | ||||
|         console.log(`Error generating title: ${error}`) | ||||
|         return fallBackTitle | ||||
|     } | ||||
| } | ||||
| @ -12,6 +12,7 @@ export type ChatHistory = { | ||||
|   role: "user" | "assistant" | "system" | ||||
|   content: string | ||||
|   image?: string | ||||
|   messageType?: string | ||||
| }[] | ||||
| 
 | ||||
| type State = { | ||||
|  | ||||
| @ -18,12 +18,14 @@ export type Message = { | ||||
|   images?: string[] | ||||
|   search?: WebSearch | ||||
|   id?: string | ||||
|   messageType?: string | ||||
| } | ||||
| 
 | ||||
| export type ChatHistory = { | ||||
|   role: "user" | "assistant" | "system" | ||||
|   content: string | ||||
|   image?: string | ||||
|   image?: string, | ||||
|   messageType?: string | ||||
| }[] | ||||
| 
 | ||||
| type State = { | ||||
|  | ||||
| @ -14,5 +14,6 @@ type WebSearch = { | ||||
|     sources: any[] | ||||
|     images?: string[] | ||||
|     search?: WebSearch | ||||
|     messageType?: string | ||||
|     id?: string | ||||
|   } | ||||
							
								
								
									
										8
									
								
								src/utils/color.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/utils/color.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| export const tagColors = { | ||||
|     summary: "blue", | ||||
|     explain: "green", | ||||
|     translate: "purple", | ||||
|     custom: "orange", | ||||
|     rephrase: "yellow" | ||||
|   } | ||||
|    | ||||
							
								
								
									
										77
									
								
								src/utils/pull-ollama.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/utils/pull-ollama.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| import { setBadgeBackgroundColor, setBadgeText, setTitle } from "@/utils/action" | ||||
| 
 | ||||
| 
 | ||||
| export const progressHuman = (completed: number, total: number) => { | ||||
|     return ((completed / total) * 100).toFixed(0) + "%" | ||||
| } | ||||
| 
 | ||||
| export const clearBadge = () => { | ||||
|     setBadgeText({ text: "" }) | ||||
|     setTitle({ title: "" }) | ||||
| } | ||||
| export const streamDownload = async (url: string, model: string) => { | ||||
|     url += "/api/pull" | ||||
|     const response = await fetch(url, { | ||||
|         method: "POST", | ||||
|         headers: { | ||||
|             "Content-Type": "application/json" | ||||
|         }, | ||||
|         body: JSON.stringify({ model, stream: true }) | ||||
|     }) | ||||
| 
 | ||||
|     const reader = response.body?.getReader() | ||||
| 
 | ||||
|     const decoder = new TextDecoder() | ||||
| 
 | ||||
|     let isSuccess = true | ||||
|     while (true) { | ||||
|         if (!reader) { | ||||
|             break | ||||
|         } | ||||
|         const { done, value } = await reader.read() | ||||
| 
 | ||||
|         if (done) { | ||||
|             break | ||||
|         } | ||||
| 
 | ||||
|         const text = decoder.decode(value) | ||||
|         try { | ||||
|             const json = JSON.parse(text.trim()) as { | ||||
|                 status: string | ||||
|                 total?: number | ||||
|                 completed?: number | ||||
|             } | ||||
|             if (json.total && json.completed) { | ||||
|                 setBadgeText({ | ||||
|                     text: progressHuman(json.completed, json.total) | ||||
|                 }) | ||||
|                 setBadgeBackgroundColor({ color: "#0000FF" }) | ||||
|             } else { | ||||
|                 setBadgeText({ text: "🏋️♂️" }) | ||||
|                 setBadgeBackgroundColor({ color: "#FFFFFF" }) | ||||
|             } | ||||
| 
 | ||||
|             setTitle({ title: json.status }) | ||||
| 
 | ||||
|             if (json.status === "success") { | ||||
|                 isSuccess = true | ||||
|             } | ||||
|         } catch (e) { | ||||
|             console.error(e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (isSuccess) { | ||||
|         setBadgeText({ text: "✅" }) | ||||
|         setBadgeBackgroundColor({ color: "#00FF00" }) | ||||
|         setTitle({ title: "Model pulled successfully" }) | ||||
|     } else { | ||||
|         setBadgeText({ text: "❌" }) | ||||
|         setBadgeBackgroundColor({ color: "#FF0000" }) | ||||
|         setTitle({ title: "Model pull failed" }) | ||||
|     } | ||||
| 
 | ||||
|     setTimeout(() => { | ||||
|         clearBadge() | ||||
|     }, 5000) | ||||
| } | ||||
| @ -50,7 +50,7 @@ export default defineConfig({ | ||||
|   outDir: "build", | ||||
| 
 | ||||
|   manifest: { | ||||
|     version: "1.1.16", | ||||
|     version: "1.2.0", | ||||
|     name: | ||||
|       process.env.TARGET === "firefox" | ||||
|         ? "Page Assist - A Web UI for Local AI Models" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user