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"
|
||||
|
@ -84,5 +84,11 @@
|
||||
}
|
||||
},
|
||||
"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
|
||||
) {
|
||||
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 = () => {
|
||||
}
|
||||
})
|
||||
|
||||
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>
|
||||
<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,6 +1079,18 @@ export const useMessage = () => {
|
||||
signal = controller.signal
|
||||
}
|
||||
|
||||
// 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,
|
||||
messageType
|
||||
)
|
||||
} else {
|
||||
if (chatMode === "normal") {
|
||||
if (webSearch) {
|
||||
await searchChatMode(
|
||||
@ -917,6 +1126,7 @@ export const useMessage = () => {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stopStreamingRequest = () => {
|
||||
if (isEmbedding) {
|
||||
@ -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