Merge pull request #274 from n4ze3m/next

v1.3.8
This commit is contained in:
Muhammed Nazeem 2024-12-15 13:30:14 +05:30 committed by GitHub
commit 216b21de2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 851 additions and 201 deletions

View File

@ -2,8 +2,7 @@
[![Join dialoqbase #welcome](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/bu54382uBd) [![Join dialoqbase #welcome](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/bu54382uBd)
Page Assist is an open-source Chrome Extension that provides a Sidebar and Web UI for your Local AI model. It allows you to interact with your model from any webpage. Page Assist is an open-source browser extension that provides a sidebar and web UI for your local AI model. It allows you to interact with your model from any webpage.
## Installation ## Installation
Page Assist supports Chromium-based browsers like Chrome, Brave, and Edge, as well as Firefox. Page Assist supports Chromium-based browsers like Chrome, Brave, and Edge, as well as Firefox.

BIN
bun.lockb

Binary file not shown.

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Venligst vælg en model for at forsæætte", "validationSelectModel": "Venligst vælg en model for at forsæætte",
"deleteHistoryConfirmation": "Er du sikker på at du vil slette denne historik?", "deleteHistoryConfirmation": "Er du sikker på at du vil slette denne historik?",
"editHistoryTitle": "Indtast en ny titel", "editHistoryTitle": "Indtast en ny titel",
"temporaryChat": "Midlertidig Chat" "temporaryChat": "Midlertidig Chat",
"more": {
"copy": {
"group": "Kopier",
"asText": "Kopier som tekst",
"asMarkdown": "Kopier som Markdown",
"success": "Kopieret til udklipsholder!"
},
"download": {
"group": "Download",
"text": "Tekstfil (.txt)",
"markdown": "Markdown (.md)",
"json": "JSON-fil (.json)"
},
"share": "Del"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "SearXNG URL" "label": "SearXNG URL"
} }
},
"braveApi": {
"label": "Brave API Nøgle",
"placeholder": "Indtast din Brave API nøgle"
} }
}, },
"system": { "system": {
"heading": "Systemindstillinger", "heading": "Systemindstillinger",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Slet Chathistorik", "label": "System Nulstilling",
"button": "Slet", "button": "Nulstil Alt",
"confirm": "Er du sikker på, at du vil slette din chathistorik? Denne handling kan ikke fortrydes." "confirm": "Er du sikker på, at du vil udføre en systemnulstilling? Dette vil slette alle data og kan ikke fortrydes."
}, },
"export": { "export": {
"label": "Eksporter chathistorik, vidensbase og prompts", "label": "Eksporter chathistorik, vidensbase og prompts",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Bitte wähle ein Modell aus, um fortzufahren", "validationSelectModel": "Bitte wähle ein Modell aus, um fortzufahren",
"deleteHistoryConfirmation": "Bist du sicher, dass du diesen Verlauf löschen möchtest?", "deleteHistoryConfirmation": "Bist du sicher, dass du diesen Verlauf löschen möchtest?",
"editHistoryTitle": "Gib einen neuen Titel ein", "editHistoryTitle": "Gib einen neuen Titel ein",
"temporaryChat": "Temporärer Chat" "temporaryChat": "Temporärer Chat",
"more": {
"copy": {
"group": "Kopieren",
"asText": "Als Text kopieren",
"asMarkdown": "Als Markdown kopieren",
"success": "In die Zwischenablage kopiert!"
},
"download": {
"group": "Herunterladen",
"text": "Textdatei (.txt)",
"markdown": "Markdown (.md)",
"json": "JSON-Datei (.json)"
},
"share": "Teilen"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "SearXNG-URL" "label": "SearXNG-URL"
} }
},
"braveApi": {
"label": "Brave API-Schlüssel",
"placeholder": "Geben Sie Ihren Brave API-Schlüssel ein"
} }
}, },
"system": { "system": {
"heading": "Systemeinstellungen", "heading": "Systemeinstellungen",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Chatverlauf löschen", "label": "System zurücksetzen",
"button": "Löschen", "button": "Alles zurücksetzen",
"confirm": "Sind Sie sicher, dass Sie Ihren Chatverlauf löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden." "confirm": "Sind Sie sicher, dass Sie einen Systemreset durchführen möchten? Dies löscht alle Daten und kann nicht rückgängig gemacht werden."
}, },
"export": { "export": {
"label": "Chatverlauf, Wissensbasis und Prompts exportieren", "label": "Chatverlauf, Wissensbasis und Prompts exportieren",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Please select a model to continue", "validationSelectModel": "Please select a model to continue",
"deleteHistoryConfirmation": "Are you sure you want to delete this history?", "deleteHistoryConfirmation": "Are you sure you want to delete this history?",
"editHistoryTitle": "Enter a new title", "editHistoryTitle": "Enter a new title",
"temporaryChat": "Temporary Chat" "temporaryChat": "Temporary Chat",
"more": {
"copy": {
"group": "Copy",
"asText": "Copy as Text",
"asMarkdown": "Copy as Markdown",
"success": "Copied to clipboard!"
},
"download": {
"group": "Download",
"text": "Text File (.txt)",
"markdown": "Markdown (.md)",
"json": "JSON File (.json)"
},
"share": "Share"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "SearXNG URL" "label": "SearXNG URL"
} }
},
"braveApi": {
"label": "Brave API Key",
"placeholder": "Enter your Brave API key"
} }
}, },
"system": { "system": {
"heading": "System Settings", "heading": "System Settings",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Delete Chat History", "label": "System Reset",
"button": "Delete", "button": "Reset All",
"confirm": "Are you sure you want to delete your chat history? This action cannot be undone." "confirm": "Are you sure you want to perform a system reset? This will clear all data and cannot be undone."
}, },
"export": { "export": {
"label": "Export Chat History, Knowledge Base, and Prompts", "label": "Export Chat History, Knowledge Base, and Prompts",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Selecione un modelo para continuar", "validationSelectModel": "Selecione un modelo para continuar",
"deleteHistoryConfirmation": "¿Esta seguro que quiere borrar éste histórico?", "deleteHistoryConfirmation": "¿Esta seguro que quiere borrar éste histórico?",
"editHistoryTitle": "Ingrese un nuevo título", "editHistoryTitle": "Ingrese un nuevo título",
"temporaryChat": "Chat Temporal" "temporaryChat": "Chat Temporal",
"more": {
"copy": {
"group": "Copiar",
"asText": "Copiar como Texto",
"asMarkdown": "Copiar como Markdown",
"success": "¡Copiado al portapapeles!"
},
"download": {
"group": "Descargar",
"text": "Archivo de Texto (.txt)",
"markdown": "Markdown (.md)",
"json": "Archivo JSON (.json)"
},
"share": "Compartir"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "URL de SearXNG" "label": "URL de SearXNG"
} }
},
"braveApi": {
"label": "Clave API de Brave",
"placeholder": "Ingrese su clave API de Brave"
} }
}, },
"system": { "system": {
"heading": "Configuraciones del Sistema", "heading": "Configuraciones del Sistema",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Borrar Histórico del Chat", "label": "Reinicio del Sistema",
"button": "Borrar", "button": "Reiniciar Todo",
"confirm": "¿Esta seguro que desea borrar su histórico del chat? Esta acción no podra ser desecha." "confirm": "¿Está seguro de que desea realizar un reinicio del sistema? Esto borrará todos los datos y no se puede deshacer."
}, },
"export": { "export": {
"label": "Exportar Histórico del Chat, Base de Conocimiento y Prompts", "label": "Exportar Histórico del Chat, Base de Conocimiento y Prompts",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "لطفا یک مدل را برای ادامه انتخاب کنید", "validationSelectModel": "لطفا یک مدل را برای ادامه انتخاب کنید",
"deleteHistoryConfirmation": "آیا مطمئن هستید که می خواهید این تاریخچه را حذف کنید؟", "deleteHistoryConfirmation": "آیا مطمئن هستید که می خواهید این تاریخچه را حذف کنید؟",
"editHistoryTitle": "یک عنوان جدید وارد کنید", "editHistoryTitle": "یک عنوان جدید وارد کنید",
"temporaryChat": "گپ موقت" "temporaryChat": "گپ موقت",
"more": {
"copy": {
"group": "کپی",
"asText": "کپی به صورت متن",
"asMarkdown": "کپی به صورت مارک‌داون",
"success": "در کلیپ‌بورد کپی شد!"
},
"download": {
"group": "دانلود",
"text": "فایل متنی (.txt)",
"markdown": "مارک‌داون (.md)",
"json": "فایل JSON (.json)"
},
"share": "اشتراک‌گذاری"
}
} }

View File

@ -67,14 +67,18 @@
"url": { "url": {
"label": "آدرس SearXNG" "label": "آدرس SearXNG"
} }
},
"braveApi": {
"label": "کلید API بریو",
"placeholder": "کلید API بریو خود را وارد کنید"
} }
}, },
"system": { "system": {
"heading": "تنظیمات سیستم", "heading": "تنظیمات سیستم",
"deleteChatHistory": { "deleteChatHistory": {
"label": "حذف تاریخچه گفتگو", "label": "بازنشانی سیستم",
"button": "حذف", "button": "بازنشانی همه",
"confirm": "آیا مطمئن هستید که می خواهید تاریخچه گفتگوهای خود را حذف کنید؟ این عمل قابل برگشت نیست." "confirm": "آیا مطمئن هستید که می‌خواهید بازنشانی سیستم را انجام دهید؟ این کار تمام داده‌ها را پاک می‌کند و غیرقابل برگشت است."
}, },
"export": { "export": {
"label": "تاریخچه گپ، پایگاه دانش و پرامپت‌ها را اکسپورت کنید", "label": "تاریخچه گپ، پایگاه دانش و پرامپت‌ها را اکسپورت کنید",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Veuillez sélectionner un modèle pour continuer", "validationSelectModel": "Veuillez sélectionner un modèle pour continuer",
"deleteHistoryConfirmation": "Êtes-vous sûr de vouloir supprimer cette historique ?", "deleteHistoryConfirmation": "Êtes-vous sûr de vouloir supprimer cette historique ?",
"editHistoryTitle": "Entrez un nouveau titre", "editHistoryTitle": "Entrez un nouveau titre",
"temporaryChat": "Chat temporaire" "temporaryChat": "Chat temporaire",
"more": {
"copy": {
"group": "Copier",
"asText": "Copier en texte",
"asMarkdown": "Copier en Markdown",
"success": "Copié dans le presse-papiers !"
},
"download": {
"group": "Télécharger",
"text": "Fichier texte (.txt)",
"markdown": "Markdown (.md)",
"json": "Fichier JSON (.json)"
},
"share": "Partager"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "URL SearXNG" "label": "URL SearXNG"
} }
},
"braveApi": {
"label": "Clé API Brave",
"placeholder": "Entrez votre clé API Brave"
} }
}, },
"system": { "system": {
"heading": "Les paramètres du système", "heading": "Les paramètres du système",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Supprimer l'historique du chat", "label": "Réinitialisation du système",
"button": "Supprimer", "button": "Tout réinitialiser",
"confirm": "Êtes-vous sûr de vouloir supprimer l'historique de votre chat? Cette action ne peut pas être annulée." "confirm": "Êtes-vous sûr de vouloir effectuer une réinitialisation du système ? Cela effacera toutes les données et ne pourra pas être annulé."
}, },
"export": { "export": {
"label": "Exporter l'historique du chat, la base de connaissances et les invites", "label": "Exporter l'historique du chat, la base de connaissances et les invites",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Scegliere un modello per continuare", "validationSelectModel": "Scegliere un modello per continuare",
"deleteHistoryConfirmation": "Sei sicuro che vuoi eliminare la cronologia?", "deleteHistoryConfirmation": "Sei sicuro che vuoi eliminare la cronologia?",
"editHistoryTitle": "Inserisci un nuovo titolo", "editHistoryTitle": "Inserisci un nuovo titolo",
"temporaryChat": "Chat Temporanea" "temporaryChat": "Chat Temporanea",
"more": {
"copy": {
"group": "Copia",
"asText": "Copia come Testo",
"asMarkdown": "Copia come Markdown",
"success": "Copiato negli appunti!"
},
"download": {
"group": "Scarica",
"text": "File di Testo (.txt)",
"markdown": "Markdown (.md)",
"json": "File JSON (.json)"
},
"share": "Condividi"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "URL SearXNG" "label": "URL SearXNG"
} }
},
"braveApi": {
"label": "Chiave API Brave",
"placeholder": "Inserisci la tua chiave API Brave"
} }
}, },
"system": { "system": {
"heading": "Impostazioni di Sistema", "heading": "Impostazioni di Sistema",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Elimina cronologia Chat", "label": "Reset del Sistema",
"button": "Elimina", "button": "Reset Totale",
"confirm": "Sei sicuro che vuoi eliminare la tua cronologia delle chat? Questa azione non può essere annullata." "confirm": "Sei sicuro di voler eseguire un reset del sistema? Questa operazione cancellerà tutti i dati e non può essere annullata."
}, },
"export": { "export": {
"label": "Esporta la cronologia Chat, Base di Conoscenza, e Prompts", "label": "Esporta la cronologia Chat, Base di Conoscenza, e Prompts",

View File

@ -1,13 +1,28 @@
{ {
"newChat": "新しいチャット", "newChat": "新しいチャット",
"selectAPrompt": "プロンプトを選択", "selectAPrompt": "プロンプトを選択",
"githubRepository": "GitHubリポジトリ", "githubRepository": "GitHubリポジトリ",
"settings": "設定", "settings": "設定",
"sidebarTitle": "チャット履歴", "sidebarTitle": "チャット履歴",
"error": "エラー", "error": "エラー",
"somethingWentWrong": "何かが間違っています", "somethingWentWrong": "何かが間違っています",
"validationSelectModel": "続行するにはモデルを選択してください", "validationSelectModel": "続行するにはモデルを選択してください",
"deleteHistoryConfirmation": "この履歴を削除しますか?", "deleteHistoryConfirmation": "この履歴を削除しますか?",
"editHistoryTitle": "新しいタイトルを入力", "editHistoryTitle": "新しいタイトルを入力",
"temporaryChat": "一時的なチャット" "temporaryChat": "一時的なチャット",
"more": {
"copy": {
"group": "コピー",
"asText": "テキストとしてコピー",
"asMarkdown": "Markdownとしてコピー",
"success": "クリップボードにコピーしました!"
},
"download": {
"group": "ダウンロード",
"text": "テキストファイル (.txt)",
"markdown": "Markdownファイル (.md)",
"json": "JSONファイル (.json)"
},
"share": "共有"
} }
}

View File

@ -73,14 +73,18 @@
"url": { "url": {
"label": "SearXNG URL" "label": "SearXNG URL"
} }
},
"braveApi": {
"label": "Brave APIキー",
"placeholder": "Brave APIキーを入力してください"
} }
}, },
"system": { "system": {
"heading": "システム設定", "heading": "システム設定",
"deleteChatHistory": { "deleteChatHistory": {
"label": "チャット履歴を削除する", "label": "システムリセット",
"button": "削除", "button": "すべてリセット",
"confirm": "チャット履歴を削除してもよろしいですか?この操作は元に戻せません。" "confirm": "システムリセットを実行してもよろしいですか?すべてのデータが消去され、元に戻すことはできません。"
}, },
"export": { "export": {
"label": "チャット履歴、知識ベース、プロンプトをエクスポート", "label": "チャット履歴、知識ベース、プロンプトをエクスポート",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "계속하려면 모델을 선택하세요", "validationSelectModel": "계속하려면 모델을 선택하세요",
"deleteHistoryConfirmation": "이 기록을 삭제하시겠습니까?", "deleteHistoryConfirmation": "이 기록을 삭제하시겠습니까?",
"editHistoryTitle": "새 제목 입력", "editHistoryTitle": "새 제목 입력",
"temporaryChat": "임시 채팅" "temporaryChat": "임시 채팅",
"more": {
"copy": {
"group": "복사",
"asText": "텍스트로 복사",
"asMarkdown": "마크다운으로 복사",
"success": "클립보드에 복사되었습니다!"
},
"download": {
"group": "다운로드",
"text": "텍스트 파일 (.txt)",
"markdown": "마크다운 (.md)",
"json": "JSON 파일 (.json)"
},
"share": "공유"
}
} }

View File

@ -73,14 +73,18 @@
"url": { "url": {
"label": "SearXNG URL" "label": "SearXNG URL"
} }
},
"braveApi": {
"label": "Brave API 키",
"placeholder": "Brave API 키를 입력하세요"
} }
}, },
"system": { "system": {
"heading": "시스템 설정", "heading": "시스템 설정",
"deleteChatHistory": { "deleteChatHistory": {
"label": "채팅 기록 삭제", "label": "시스템 초기화",
"button": "삭제", "button": "전체 초기화",
"confirm": "채팅 기록을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다." "confirm": "시스템을 초기화하시겠습니까? 모든 데이터가 삭제되며 되돌릴 수 없습니다."
}, },
"export": { "export": {
"label": "채팅 기록, 지식 베이스, 프롬프트 내보내기", "label": "채팅 기록, 지식 베이스, 프롬프트 내보내기",

View File

@ -9,5 +9,20 @@
"deleteHistoryConfirmation": "നിങ്ങളുടെ ചാറ്റ് ചരിത്രം ഇല്ലാതാക്കണമെന്ന് തീർച്ചയാണോ?", "deleteHistoryConfirmation": "നിങ്ങളുടെ ചാറ്റ് ചരിത്രം ഇല്ലാതാക്കണമെന്ന് തീർച്ചയാണോ?",
"editHistoryTitle": "ചാറ്റ് title എഡിറ്റുചെയ്യുക", "editHistoryTitle": "ചാറ്റ് title എഡിറ്റുചെയ്യുക",
"validationSelectModel": "തുടരുന്നതിന് ഒരു മോഡല്‍ തിരഞ്ഞെടുക്കുക", "validationSelectModel": "തുടരുന്നതിന് ഒരു മോഡല്‍ തിരഞ്ഞെടുക്കുക",
"temporaryChat": "താൽക്കാലിക ചാറ്റ്" "temporaryChat": "താൽക്കാലിക ചാറ്റ്",
"more": {
"copy": {
"group": "പകർത്തുക",
"asText": "ടെക്സ്റ്റായി പകർത്തുക",
"asMarkdown": "മാർക്ക്ഡൗൺ ആയി പകർത്തുക",
"success": "ക്ലിപ്പ്ബോർഡിലേക്ക് പകർത്തി!"
},
"download": {
"group": "ഡൗൺലോഡ്",
"text": "ടെക്സ്റ്റ് ഫയൽ (.txt)",
"markdown": "മാർക്ക്ഡൗൺ (.md)",
"json": "JSON ഫയൽ (.json)"
},
"share": "പങ്കുവയ്ക്കുക"
}
} }

View File

@ -73,14 +73,18 @@
"url": { "url": {
"label": "SearXNG URL" "label": "SearXNG URL"
} }
},
"braveApi": {
"label": "ബ്രേവ് API കീ",
"placeholder": "നിങ്ങളുടെ ബ്രേവ് API കീ നൽകുക"
} }
}, },
"system": { "system": {
"heading": "സിസ്റ്റം ക്രമീകരണങ്ങൾ", "heading": "സിസ്റ്റം ക്രമീകരണങ്ങൾ",
"deleteChatHistory": { "deleteChatHistory": {
"label": "ചാറ്റ് ചരിത്രം ഇല്ലാതാക്കുക", "label": "സിസ്റ്റം റീസെറ്റ്",
"button": "ഇല്ലാതാക്കുക", "button": "എല്ലാം റീസെറ്റ് ചെയ്യുക",
"confirm": "നിങ്ങളുടെ ചാറ്റ് ചരിത്രം ഇല്ലാതാക്കണമെന്ന് ഉറപ്പാണോ? ഈ പ്രവർത്തനം പിന്നീട് പിന്വലിക്കാനാവില്ല." "confirm": "നിങ്ങൾക്ക് സിസ്റ്റം റീസെറ്റ് നടത്താൻ തീർച്ചയാണോ? ഇത് എല്ലാ ഡാറ്റയും മായ്ക്കും, പിന്നീട് പുനഃസ്ഥാപിക്കാൻ കഴിയില്ല."
}, },
"export": { "export": {
"label": "ചാറ്റ് ചരിത്രം, അറിവ് അടിസ്ഥാനം, പ്രോംപ്റ്റുകൾ എക്സ്പോർട്ട് ചെയ്യുക", "label": "ചാറ്റ് ചരിത്രം, അറിവ് അടിസ്ഥാനം, പ്രോംപ്റ്റുകൾ എക്സ്പോർട്ട് ചെയ്യുക",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Vennligst velg en modell for å fortsette", "validationSelectModel": "Vennligst velg en modell for å fortsette",
"deleteHistoryConfirmation": "Er du sikker på at du vil slette denne historikken?", "deleteHistoryConfirmation": "Er du sikker på at du vil slette denne historikken?",
"editHistoryTitle": "Skriv inn en ny tittel", "editHistoryTitle": "Skriv inn en ny tittel",
"temporaryChat": "Midlertidig Chat" "temporaryChat": "Midlertidig Chat",
"more": {
"copy": {
"group": "Kopier",
"asText": "Kopier som tekst",
"asMarkdown": "Kopier som Markdown",
"success": "Kopiert til utklippstavlen!"
},
"download": {
"group": "Last ned",
"text": "Tekstfil (.txt)",
"markdown": "Markdown (.md)",
"json": "JSON-fil (.json)"
},
"share": "Del"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "SearXNG URL" "label": "SearXNG URL"
} }
},
"braveApi": {
"label": "Brave API Nøkkel",
"placeholder": "Skriv inn din Brave API nøkkel"
} }
}, },
"system": { "system": {
"heading": "Systeminnstillinger", "heading": "Systeminnstillinger",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Slett Chathistorikk", "label": "System Tilbakestilling",
"button": "Slett", "button": "Tilbakestill Alt",
"confirm": "Er du sikker på at du vil slette chathistorikken din? Denne handlingen kan ikke angres." "confirm": "Er du sikker på at du vil utføre en system tilbakestilling? Dette vil slette alle data og kan ikke angres."
}, },
"export": { "export": {
"label": "Eksporter chathistorikk, kunnskapsbase og prompts", "label": "Eksporter chathistorikk, kunnskapsbase og prompts",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Por favor, selecione um modelo para continuar", "validationSelectModel": "Por favor, selecione um modelo para continuar",
"deleteHistoryConfirmation": "Tem certeza de que deseja excluir este histórico?", "deleteHistoryConfirmation": "Tem certeza de que deseja excluir este histórico?",
"editHistoryTitle": "Digite um novo título", "editHistoryTitle": "Digite um novo título",
"temporaryChat": "Chat Temporário" "temporaryChat": "Chat Temporário",
"more": {
"copy": {
"group": "Copiar",
"asText": "Copiar como Texto",
"asMarkdown": "Copiar como Markdown",
"success": "Copiado para área de transferência!"
},
"download": {
"group": "Baixar",
"text": "Arquivo de Texto (.txt)",
"markdown": "Markdown (.md)",
"json": "Arquivo JSON (.json)"
},
"share": "Compartilhar"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "URL do SearXNG" "label": "URL do SearXNG"
} }
},
"braveApi": {
"label": "Chave da API do Brave",
"placeholder": "Digite sua chave da API do Brave"
} }
}, },
"system": { "system": {
"heading": "Configurações do Sistema", "heading": "Configurações do Sistema",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Excluir Histórico de Chat", "label": "Reiniciar Sistema",
"button": "Excluir", "button": "Reiniciar Tudo",
"confirm": "Tem certeza de que deseja excluir seu histórico de chat? Esta ação não pode ser desfeita." "confirm": "Tem certeza que deseja realizar um reinício do sistema? Isso irá apagar todos os dados e não poderá ser desfeito."
}, },
"export": { "export": {
"label": "Exportar Histórico de Chat, Base de Conhecimento e Prompts", "label": "Exportar Histórico de Chat, Base de Conhecimento e Prompts",

View File

@ -9,5 +9,19 @@
"validationSelectModel": "Пожалуйста, выберите модель, чтобы продолжить", "validationSelectModel": "Пожалуйста, выберите модель, чтобы продолжить",
"deleteHistoryConfirmation": "Вы уверены, что хотите удалить эту историю?", "deleteHistoryConfirmation": "Вы уверены, что хотите удалить эту историю?",
"editHistoryTitle": "Введите новое название", "editHistoryTitle": "Введите новое название",
"temporaryChat": "Временный чат" "temporaryChat": "Временный чат",
} "more": {
"copy": {
"group": "Копировать",
"asText": "Копировать как текст",
"asMarkdown": "Копировать как Markdown",
"success": "Скопировано в буфер обмена!"
},
"download": {
"group": "Скачать",
"text": "Текстовый файл (.txt)",
"markdown": "Markdown (.md)",
"json": "JSON файл (.json)"
},
"share": "Поделиться"
}}

View File

@ -71,14 +71,18 @@
"url": { "url": {
"label": "URL-адрес SearXNG" "label": "URL-адрес SearXNG"
} }
},
"braveApi": {
"label": "API-ключ Brave",
"placeholder": "Введите ваш API-ключ Brave"
} }
}, },
"system": { "system": {
"heading": "Настройки системы", "heading": "Настройки системы",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Удалить историю чата", "label": "Сброс системы",
"button": "Удалить", "button": "Сбросить все",
"confirm": "Вы уверены, что хотите удалить историю чата? Это действие нельзя отменить." "confirm": "Вы уверены, что хотите выполнить сброс системы? Это удалит все данные без возможности восстановления."
}, },
"export": { "export": {
"label": "Экспорт истории чата, базы знаний и подсказок", "label": "Экспорт истории чата, базы знаний и подсказок",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Vänligen välj en modell för att fortsätta", "validationSelectModel": "Vänligen välj en modell för att fortsätta",
"deleteHistoryConfirmation": "Är du säker på att du vill radera denna historik?", "deleteHistoryConfirmation": "Är du säker på att du vill radera denna historik?",
"editHistoryTitle": "Ange en ny titel", "editHistoryTitle": "Ange en ny titel",
"temporaryChat": "Tillfällig chatt" "temporaryChat": "Tillfällig chatt",
"more": {
"copy": {
"group": "Kopiera",
"asText": "Kopiera som text",
"asMarkdown": "Kopiera som Markdown",
"success": "Kopierat till urklipp!"
},
"download": {
"group": "Ladda ner",
"text": "Textfil (.txt)",
"markdown": "Markdown (.md)",
"json": "JSON-fil (.json)"
},
"share": "Dela"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "SearXNG URL" "label": "SearXNG URL"
} }
},
"braveApi": {
"label": "Brave API-nyckel",
"placeholder": "Ange din Brave API-nyckel"
} }
}, },
"system": { "system": {
"heading": "Systeminställningar", "heading": "Systeminställningar",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Radera Chatt Historik", "label": "Systemåterställning",
"button": "Radera", "button": "Återställ Allt",
"confirm": "Är du säker på att du vill radera din chatthistorik? Denna åtgärd kan inte ångras." "confirm": "Är du säker på att du vill utföra en systemåterställning? Detta kommer att radera all data och kan inte ångras."
}, },
"export": { "export": {
"label": "Exportera chatthistorik, kunskapsbas och instruktioner", "label": "Exportera chatthistorik, kunskapsbas och instruktioner",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "Будь ласка, виберіть модель для продовження", "validationSelectModel": "Будь ласка, виберіть модель для продовження",
"deleteHistoryConfirmation": "Ви впевнені, що хочете видалити цю історію?", "deleteHistoryConfirmation": "Ви впевнені, що хочете видалити цю історію?",
"editHistoryTitle": "Введіть нову назву", "editHistoryTitle": "Введіть нову назву",
"temporaryChat": "Тимчасовий чат" "temporaryChat": "Тимчасовий чат",
"more": {
"copy": {
"group": "Копіювати",
"asText": "Копіювати як текст",
"asMarkdown": "Копіювати як Markdown",
"success": "Скопійовано в буфер обміну!"
},
"download": {
"group": "Завантажити",
"text": "Текстовий файл (.txt)",
"markdown": "Markdown (.md)",
"json": "JSON файл (.json)"
},
"share": "Поділитися"
}
} }

View File

@ -70,14 +70,18 @@
"url": { "url": {
"label": "SearXNG URL-адреса" "label": "SearXNG URL-адреса"
} }
},
"braveApi": {
"label": "Ключ API Brave",
"placeholder": "Введіть ваш ключ API Brave"
} }
}, },
"system": { "system": {
"heading": "Системні налаштування", "heading": "Системні налаштування",
"deleteChatHistory": { "deleteChatHistory": {
"label": "Видалити історію чату", "label": "Скидання системи",
"button": "Видалити", "button": "Скинути все",
"confirm": "Ви впевнені, що хочете видалити історію чату? Ця дія не може бути відвернута." "confirm": "Ви впевнені, що хочете виконати скидання системи? Це призведе до видалення всіх даних, і цю дію неможливо буде скасувати."
}, },
"export": { "export": {
"label": "Експорт історії чату, бази знань та запитів", "label": "Експорт історії чату, бази знань та запитів",

View File

@ -9,5 +9,20 @@
"validationSelectModel": "请选择一个模型以继续", "validationSelectModel": "请选择一个模型以继续",
"deleteHistoryConfirmation": "你确定要删除这个历史记录吗?", "deleteHistoryConfirmation": "你确定要删除这个历史记录吗?",
"editHistoryTitle": "输入一个新的标题", "editHistoryTitle": "输入一个新的标题",
"temporaryChat": "临时聊天" "temporaryChat": "临时聊天",
"more": {
"copy": {
"group": "复制",
"asText": "复制为文本",
"asMarkdown": "复制为 Markdown",
"success": "已复制到剪贴板!"
},
"download": {
"group": "下载",
"text": "文本文件 (.txt)",
"markdown": "Markdown 文件 (.md)",
"json": "JSON 文件 (.json)"
},
"share": "分享"
}
} }

View File

@ -73,14 +73,18 @@
"url": { "url": {
"label": "SearXNG 网址" "label": "SearXNG 网址"
} }
},
"braveApi": {
"label": "Brave API 密钥",
"placeholder": "输入您的 Brave API 密钥"
} }
}, },
"system": { "system": {
"heading": "系统设置", "heading": "系统设置",
"deleteChatHistory": { "deleteChatHistory": {
"label": "删除聊天记录", "label": "系统重置",
"button": "删除", "button": "全部重置",
"confirm": "您确定要删除聊天记录吗?此操作无法撤销。" "confirm": "您确定要执行系统重置吗?这将清除所有数据且无法撤消。"
}, },
"export": { "export": {
"label": "导出聊天记录、知识库和提示", "label": "导出聊天记录、知识库和提示",

View File

@ -14,6 +14,8 @@ import fetcher from "@/libs/fetcher"
type Props = { type Props = {
messages: Message[] messages: Message[]
historyId: string historyId: string
open: boolean
setOpen: (state: boolean) => void
} }
const reformatMessages = (messages: Message[], username: string) => { const reformatMessages = (messages: Message[], username: string) => {
@ -77,9 +79,13 @@ export const PlaygroundMessage = (
) )
} }
export const ShareBtn: React.FC<Props> = ({ messages, historyId }) => { export const ShareModal: React.FC<Props> = ({
messages,
historyId,
open,
setOpen
}) => {
const { t } = useTranslation("common") const { t } = useTranslation("common")
const [open, setOpen] = useState(false)
const [form] = Form.useForm() const [form] = Form.useForm()
const name = Form.useWatch("name", form) const name = Form.useWatch("name", form)
@ -142,75 +148,55 @@ export const ShareBtn: React.FC<Props> = ({ messages, historyId }) => {
}) })
return ( return (
<> <Modal
<Tooltip title={t("share.tooltip.share")}> title={t("share.modal.title")}
<button open={open}
onClick={() => setOpen(true)} footer={null}
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"> width={600}
<Share className="w-6 h-6" /> onCancel={() => setOpen(false)}>
</button> <Form
</Tooltip> form={form}
layout="vertical"
onFinish={createShareLink}
initialValues={{
title: t("share.form.defaultValue.title"),
name: t("share.form.defaultValue.name")
}}>
<Form.Item
name="title"
label={t("share.form.title.label")}
rules={[{ required: true, message: t("share.form.title.required") }]}>
<Input size="large" placeholder={t("share.form.title.placeholder")} />
</Form.Item>
<Form.Item
name="name"
label={t("share.form.name.label")}
rules={[{ required: true, message: t("share.form.name.required") }]}>
<Input size="large" placeholder={t("share.form.name.placeholder")} />
</Form.Item>
<Modal <Form.Item>
title={t("share.modal.title")} <div className="max-h-[180px] overflow-x-auto border dark:border-gray-700 rounded-md p-2">
open={open} <div className="flex flex-col p-3">
footer={null} {messages.map((message, index) => (
width={600} <PlaygroundMessage key={index} {...message} username={name} />
onCancel={() => setOpen(false)}> ))}
<Form
form={form}
layout="vertical"
onFinish={createShareLink}
initialValues={{
title: t("share.form.defaultValue.title"),
name: t("share.form.defaultValue.name")
}}>
<Form.Item
name="title"
label={t("share.form.title.label")}
rules={[
{ required: true, message: t("share.form.title.required") }
]}>
<Input
size="large"
placeholder={t("share.form.title.placeholder")}
/>
</Form.Item>
<Form.Item
name="name"
label={t("share.form.name.label")}
rules={[
{ required: true, message: t("share.form.name.required") }
]}>
<Input
size="large"
placeholder={t("share.form.name.placeholder")}
/>
</Form.Item>
<Form.Item>
<div className="max-h-[180px] overflow-x-auto border dark:border-gray-700 rounded-md p-2">
<div className="flex flex-col p-3">
{messages.map((message, index) => (
<PlaygroundMessage key={index} {...message} username={name} />
))}
</div>
</div> </div>
</Form.Item> </div>
</Form.Item>
<Form.Item> <Form.Item>
<div className="flex justify-end"> <div className="flex justify-end">
<button <button
type="submit" type="submit"
className="inline-flex items-center rounded-md border border-transparent bg-black px-2 py-2.5 text-md font-medium leading-4 text-white shadow-sm dark:bg-white dark:text-gray-800 disabled:opacity-50 "> className="inline-flex items-center rounded-md border border-transparent bg-black px-2 py-2.5 text-md font-medium leading-4 text-white shadow-sm dark:bg-white dark:text-gray-800 disabled:opacity-50 ">
{isPending {isPending
? t("share.form.btn.saving") ? t("share.form.btn.saving")
: t("share.form.btn.save")} : t("share.form.btn.save")}
</button> </button>
</div> </div>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>
</>
) )
} }

View File

@ -19,10 +19,10 @@ import { fetchChatModels } from "~/services/ollama"
import { useMessageOption } from "~/hooks/useMessageOption" import { useMessageOption } from "~/hooks/useMessageOption"
import { Select, Tooltip } from "antd" import { Select, Tooltip } from "antd"
import { getAllPrompts } from "@/db" import { getAllPrompts } from "@/db"
import { ShareBtn } from "~/components/Common/ShareBtn"
import { ProviderIcons } from "../Common/ProviderIcon" import { ProviderIcons } from "../Common/ProviderIcon"
import { NewChat } from "./NewChat" import { NewChat } from "./NewChat"
import { PageAssistSelect } from "../Select" import { PageAssistSelect } from "../Select"
import { MoreOptions } from "./MoreOptions"
type Props = { type Props = {
setSidebarOpen: (open: boolean) => void setSidebarOpen: (open: boolean) => void
setOpenModelSettings: (open: boolean) => void setOpenModelSettings: (open: boolean) => void
@ -50,7 +50,11 @@ export const Header: React.FC<Props> = ({
historyId, historyId,
temporaryChat temporaryChat
} = useMessageOption() } = useMessageOption()
const { data: models, isLoading: isModelsLoading, refetch } = useQuery({ const {
data: models,
isLoading: isModelsLoading,
refetch
} = useQuery({
queryKey: ["fetchModel"], queryKey: ["fetchModel"],
queryFn: () => fetchChatModels({ returnEmpty: true }), queryFn: () => fetchChatModels({ returnEmpty: true }),
refetchIntervalInBackground: false, refetchIntervalInBackground: false,
@ -134,7 +138,6 @@ export const Header: React.FC<Props> = ({
), ),
value: model.model value: model.model
}))} }))}
onRefresh={() => { onRefresh={() => {
refetch() refetch()
}} }}
@ -189,6 +192,19 @@ export const Header: React.FC<Props> = ({
<div className="flex flex-1 justify-end px-4"> <div className="flex flex-1 justify-end px-4">
<div className="ml-4 flex items-center md:ml-6"> <div className="ml-4 flex items-center md:ml-6">
<div className="flex gap-4 items-center"> <div className="flex gap-4 items-center">
{/* {pathname === "/" &&
messages.length > 0 &&
!streaming &&
shareModeEnabled && (
<ShareBtn historyId={historyId} messages={messages} />
)} */}
{messages.length > 0 && !streaming && (
<MoreOptions
shareModeEnabled={shareModeEnabled}
historyId={historyId}
messages={messages}
/>
)}
{!hideCurrentChatModelSettings && ( {!hideCurrentChatModelSettings && (
<Tooltip title={t("common:currentChatModelSettings")}> <Tooltip title={t("common:currentChatModelSettings")}>
<button <button
@ -198,12 +214,6 @@ export const Header: React.FC<Props> = ({
</button> </button>
</Tooltip> </Tooltip>
)} )}
{pathname === "/" &&
messages.length > 0 &&
!streaming &&
shareModeEnabled && (
<ShareBtn historyId={historyId} messages={messages} />
)}
<Tooltip title={t("githubRepository")}> <Tooltip title={t("githubRepository")}>
<a <a
href="https://github.com/n4ze3m/page-assist" href="https://github.com/n4ze3m/page-assist"

View File

@ -0,0 +1,163 @@
import {
MoreHorizontal,
FileText,
Share2,
FileJson,
FileCode
} from "lucide-react"
import { Dropdown, MenuProps, message } from "antd"
import { Message } from "@/types/message"
import { useState } from "react"
import { ShareModal } from "../Common/ShareModal"
import { useTranslation } from "react-i18next"
interface MoreOptionsProps {
messages: Message[]
historyId: string
shareModeEnabled: boolean
}
const formatAsText = (messages: Message[]) => {
return messages
.map((msg) => {
const text = `${msg.isBot ? msg.name : "You"}: ${msg.message}`
return text
})
.join("\n\n")
}
const formatAsMarkdown = (messages: Message[]) => {
return messages
.map((msg) => {
let content = `**${msg.isBot ? msg.name : "You"}**:\n${msg.message}`
if (msg.images && msg.images.length > 0) {
const imageMarkdown = msg.images
.filter((img) => img.length > 0)
.map((img) => `\n\n![Image](${img})`)
.join("\n")
content += imageMarkdown
}
return content
})
.join("\n\n")
}
const downloadFile = (content: string, filename: string) => {
const blob = new Blob([content], { type: "text/plain;charset=utf-8" })
const url = URL.createObjectURL(blob)
const link = document.createElement("a")
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
}
export const MoreOptions = ({
shareModeEnabled = false,
historyId,
messages
}: MoreOptionsProps) => {
const { t } = useTranslation("option")
const [onShareOpen, setOnShareOpen] = useState(false)
const baseItems: MenuProps["items"] = [
{
type: "group",
label: t("more.copy.group"),
children: [
{
key: "copy-text",
label: t("more.copy.asText"),
icon: <FileText className="w-4 h-4" />,
onClick: () => {
navigator.clipboard.writeText(formatAsText(messages))
message.success(t("more.copy.success"))
}
},
{
key: "copy-markdown",
label: t("more.copy.asMarkdown"),
icon: <FileCode className="w-4 h-4" />,
onClick: () => {
navigator.clipboard.writeText(formatAsMarkdown(messages))
message.success(t("more.copy.success"))
}
}
]
},
{
type: "divider"
},
{
type: "group",
label: t("more.download.group"),
children: [
{
key: "download-txt",
label: t("more.download.text"),
icon: <FileText className="w-4 h-4" />,
onClick: () => {
downloadFile(formatAsText(messages), "chat.txt")
}
},
{
key: "download-md",
label: t("more.download.markdown"),
icon: <FileCode className="w-4 h-4" />,
onClick: () => {
downloadFile(formatAsMarkdown(messages), "chat.md")
}
},
{
key: "download-json",
label: t("more.download.json"),
icon: <FileJson className="w-4 h-4" />,
onClick: () => {
const jsonContent = JSON.stringify(messages, null, 2)
downloadFile(jsonContent, "chat.json")
}
}
]
}
]
const shareItem = {
type: "divider"
} as const
const shareOption = {
key: "share",
label: t("more.share"),
icon: <Share2 className="w-4 h-4" />,
onClick: () => {
setOnShareOpen(true)
}
}
const items = shareModeEnabled
? [...baseItems, shareItem, shareOption]
: baseItems
return (
<>
<Dropdown
menu={{
items
}}
trigger={["click"]}
placement="bottomRight">
<button className="!text-gray-500 dark:text-gray-300 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<MoreHorizontal className="w-6 h-6" />
</button>
</Dropdown>
<ShareModal
open={onShareOpen}
historyId={historyId}
messages={messages}
setOpen={setOnShareOpen}
/>
</>
)
}

View File

@ -27,7 +27,7 @@ export const PlaygroundChat = () => {
<> <>
<div <div
ref={containerRef} ref={containerRef}
className="custom-scrollbar grow flex flex-col md:translate-x-0 transition-transform duration-300 ease-in-out overflow-y-auto h-[calc(100vh-160px)]"> className="custom-scrollbar grow flex flex-col md:translate-x-0 transition-transform duration-300 ease-in-out overflow-y-auto h-[calc(100vh-160px)]">
{messages.length === 0 && ( {messages.length === 0 && (
<div className="mt-32"> <div className="mt-32">
<PlaygroundEmpty /> <PlaygroundEmpty />

View File

@ -220,30 +220,7 @@ export const GeneralSettings = () => {
</h2> </h2>
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div> <div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div>
</div> </div>
<div className="flex flex-row mb-3 justify-between">
<span className="text-gray-700 dark:text-neutral-50 ">
{t("generalSettings.system.deleteChatHistory.label")}
</span>
<button
onClick={async () => {
const confirm = window.confirm(
t("generalSettings.system.deleteChatHistory.confirm")
)
if (confirm) {
const db = new PageAssitDatabase()
await db.deleteAllChatHistory()
queryClient.invalidateQueries({
queryKey: ["fetchChatHistory"]
})
clearChat()
}
}}
className="bg-red-500 dark:bg-red-600 text-white dark:text-gray-200 px-4 py-2 rounded-md">
{t("generalSettings.system.deleteChatHistory.button")}
</button>
</div>
<div className="flex flex-row mb-3 justify-between"> <div className="flex flex-row mb-3 justify-between">
<span className="text-gray-700 dark:text-neutral-50 "> <span className="text-gray-700 dark:text-neutral-50 ">
{t("generalSettings.system.export.label")} {t("generalSettings.system.export.label")}
@ -275,6 +252,38 @@ export const GeneralSettings = () => {
}} }}
/> />
</div> </div>
<div className="flex flex-row mb-3 justify-between">
<span className="text-gray-700 dark:text-neutral-50 ">
{t("generalSettings.system.deleteChatHistory.label")}
</span>
<button
onClick={async () => {
const confirm = window.confirm(
t("generalSettings.system.deleteChatHistory.confirm")
)
if (confirm) {
const db = new PageAssitDatabase()
await db.deleteAllChatHistory()
queryClient.invalidateQueries({
queryKey: ["fetchChatHistory"]
})
clearChat()
try {
await browser.storage.sync.clear()
await browser.storage.local.clear()
await browser.storage.session.clear()
} catch (e) {
console.log("Error clearing storage:", e)
}
}
}}
className="bg-red-500 dark:bg-red-600 text-white dark:text-gray-200 px-4 py-2 rounded-md">
{t("generalSettings.system.deleteChatHistory.button")}
</button>
</div>
</div> </div>
</dl> </dl>
) )

View File

@ -2,7 +2,7 @@ import { SaveButton } from "@/components/Common/SaveButton"
import { getSearchSettings, setSearchSettings } from "@/services/search" import { getSearchSettings, setSearchSettings } from "@/services/search"
import { SUPPORTED_SERACH_PROVIDERS } from "@/utils/search-provider" import { SUPPORTED_SERACH_PROVIDERS } from "@/utils/search-provider"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { useQuery, useQueryClient } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
import { Select, Skeleton, Switch, InputNumber, Input } from "antd" import { Select, Skeleton, Switch, InputNumber, Input } from "antd"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
@ -16,7 +16,8 @@ export const SearchModeSettings = () => {
totalSearchResults: 0, totalSearchResults: 0,
visitSpecificWebsite: false, visitSpecificWebsite: false,
searxngURL: "", searxngURL: "",
searxngJSONMode: false searxngJSONMode: false,
braveApiKey: "",
} }
}) })
@ -81,6 +82,25 @@ export const SearchModeSettings = () => {
</div> </div>
</> </>
)} )}
{form.values.searchProvider === "brave-api" && (
<>
<div className="flex sm:flex-row flex-col space-y-4 sm:space-y-0 sm:justify-between">
<span className="text-gray-700 dark:text-neutral-50">
{t("generalSettings.webSearch.braveApi.label")}
</span>
<div>
<Input.Password
placeholder={t(
"generalSettings.webSearch.braveApi.placeholder"
)}
required
className="w-full mt-4 sm:mt-0 sm:w-[200px]"
{...form.getInputProps("braveApiKey")}
/>
</div>
</div>
</>
)}
<div className="flex sm:flex-row flex-col space-y-4 sm:space-y-0 sm:justify-between"> <div className="flex sm:flex-row flex-col space-y-4 sm:space-y-0 sm:justify-between">
<span className="text-gray-700 dark:text-neutral-50 "> <span className="text-gray-700 dark:text-neutral-50 ">
{t("generalSettings.webSearch.searchMode.label")} {t("generalSettings.webSearch.searchMode.label")}

View File

@ -145,6 +145,12 @@ export const getKnowledgeById = async (id: string) => {
export const updateKnowledgeStatus = async (id: string, status: string) => { export const updateKnowledgeStatus = async (id: string, status: string) => {
const db = new PageAssistKnowledge() const db = new PageAssistKnowledge()
const knowledge = await db.getById(id) const knowledge = await db.getById(id)
if(status === "finished") {
knowledge.source = knowledge.source.map(e => ({
...e,
content: undefined,
}))
}
await db.update({ await db.update({
...knowledge, ...knowledge,
status status

View File

@ -108,7 +108,7 @@ export const saveMessageOnError = async ({
setHistoryId(newHistoryId.id) setHistoryId(newHistoryId.id)
await setLastUsedChatModel(newHistoryId.id, selectedModel) await setLastUsedChatModel(newHistoryId.id, selectedModel)
if (prompt_id || prompt_content) { if (prompt_id || prompt_content) {
await setLastUsedChatSystemPrompt(historyId, { prompt_content, prompt_id }) await setLastUsedChatSystemPrompt(newHistoryId.id, { prompt_content, prompt_id })
} }
} }
@ -203,7 +203,7 @@ export const saveMessageOnSuccess = async ({
setHistoryId(newHistoryId.id) setHistoryId(newHistoryId.id)
await setLastUsedChatModel(newHistoryId.id, selectedModel!) await setLastUsedChatModel(newHistoryId.id, selectedModel!)
if (prompt_id || prompt_content) { if (prompt_id || prompt_content) {
await setLastUsedChatSystemPrompt(historyId, { prompt_content, prompt_id }) await setLastUsedChatSystemPrompt(newHistoryId.id, { prompt_content, prompt_id })
} }
} }
} }

View File

@ -1,5 +1,7 @@
import { Storage } from "@plasmohq/storage" import { Storage } from "@plasmohq/storage"
const storage = new Storage() const storage = new Storage({
area: "local"
})
type ModelSettings = { type ModelSettings = {
f16KV?: boolean f16KV?: boolean
@ -63,7 +65,7 @@ const keys = [
"vocabOnly" "vocabOnly"
] ]
const getAllModelSettings = async () => { export const getAllModelSettings = async () => {
try { try {
const settings: ModelSettings = {} const settings: ModelSettings = {}
for (const key of keys) { for (const key of keys) {
@ -80,7 +82,7 @@ const getAllModelSettings = async () => {
} }
} }
const setModelSetting = async ( export const setModelSetting = async (
key: string, key: string,
value: string | number | boolean value: string | number | boolean
) => { ) => {
@ -144,4 +146,3 @@ export const setLastUsedChatSystemPrompt = async (
await storage.set(`lastUsedChatSystemPrompt-${historyId}`, prompt) await storage.set(`lastUsedChatSystemPrompt-${historyId}`, prompt)
} }
export { getAllModelSettings, setModelSetting }

View File

@ -1,6 +1,9 @@
import { Storage } from "@plasmohq/storage" import { Storage } from "@plasmohq/storage"
const storage = new Storage() const storage = new Storage()
const storage2 = new Storage({
area: "local"
})
const TOTAL_SEARCH_RESULTS = 2 const TOTAL_SEARCH_RESULTS = 2
const DEFAULT_PROVIDER = "google" const DEFAULT_PROVIDER = "google"
@ -80,10 +83,20 @@ export const setSearxngURL = async (searxngURL: string) => {
await storage.set("searxngURL", searxngURL) await storage.set("searxngURL", searxngURL)
} }
export const getBraveApiKey = async () => {
const braveApiKey = await storage2.get("braveApiKey")
return braveApiKey || ""
}
export const setBraveApiKey = async (braveApiKey: string) => {
await storage2.set("braveApiKey", braveApiKey)
}
export const getSearchSettings = async () => { export const getSearchSettings = async () => {
const [isSimpleInternetSearch, searchProvider, totalSearchResult, visitSpecificWebsite, const [isSimpleInternetSearch, searchProvider, totalSearchResult, visitSpecificWebsite,
searxngURL, searxngURL,
searxngJSONMode searxngJSONMode,
braveApiKey
] = ] =
await Promise.all([ await Promise.all([
getIsSimpleInternetSearch(), getIsSimpleInternetSearch(),
@ -91,7 +104,8 @@ export const getSearchSettings = async () => {
totalSearchResults(), totalSearchResults(),
getIsVisitSpecificWebsite(), getIsVisitSpecificWebsite(),
getSearxngURL(), getSearxngURL(),
isSearxngJSONMode() isSearxngJSONMode(),
getBraveApiKey()
]) ])
return { return {
@ -100,7 +114,8 @@ export const getSearchSettings = async () => {
totalSearchResults: totalSearchResult, totalSearchResults: totalSearchResult,
visitSpecificWebsite, visitSpecificWebsite,
searxngURL, searxngURL,
searxngJSONMode searxngJSONMode,
braveApiKey
} }
} }
@ -110,14 +125,16 @@ export const setSearchSettings = async ({
totalSearchResults, totalSearchResults,
visitSpecificWebsite, visitSpecificWebsite,
searxngJSONMode, searxngJSONMode,
searxngURL searxngURL,
braveApiKey
}: { }: {
isSimpleInternetSearch: boolean isSimpleInternetSearch: boolean
searchProvider: string searchProvider: string
totalSearchResults: number totalSearchResults: number
visitSpecificWebsite: boolean visitSpecificWebsite: boolean
searxngURL: string searxngURL: string
searxngJSONMode: boolean searxngJSONMode: boolean,
braveApiKey: string
}) => { }) => {
await Promise.all([ await Promise.all([
setIsSimpleInternetSearch(isSimpleInternetSearch), setIsSimpleInternetSearch(isSimpleInternetSearch),
@ -125,6 +142,7 @@ export const setSearchSettings = async ({
setTotalSearchResults(totalSearchResults), setTotalSearchResults(totalSearchResults),
setIsVisitSpecificWebsite(visitSpecificWebsite), setIsVisitSpecificWebsite(visitSpecificWebsite),
setSearxngJSONMode(searxngJSONMode), setSearxngJSONMode(searxngJSONMode),
setSearxngURL(searxngURL) setSearxngURL(searxngURL),
setBraveApiKey(braveApiKey)
]) ])
} }

View File

@ -18,5 +18,9 @@ export const SUPPORTED_SERACH_PROVIDERS = [
{ {
label: "Searxng", label: "Searxng",
value: "searxng" value: "searxng"
},
{
label: "Brave Search API",
value: "brave-api"
} }
] ]

View File

@ -0,0 +1,128 @@
import { cleanUrl } from "~/libs/clean-url"
import { getIsSimpleInternetSearch, totalSearchResults, getBraveApiKey } from "@/services/search"
import { pageAssistEmbeddingModel } from "@/models/embedding"
import type { Document } from "@langchain/core/documents"
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
import { MemoryVectorStore } from "langchain/vectorstores/memory"
import { PageAssistHtmlLoader } from "~/loader/html"
import {
defaultEmbeddingChunkOverlap,
defaultEmbeddingChunkSize,
defaultEmbeddingModelForRag,
getOllamaURL
} from "~/services/ollama"
interface BraveAPIResult {
title: string
url: string
description: string
}
interface BraveAPIResponse {
web: {
results: BraveAPIResult[]
}
}
export const braveAPISearch = async (query: string) => {
const braveApiKey = await getBraveApiKey()
if (!braveApiKey || braveApiKey.trim() === "") {
throw new Error("Brave API key not configured")
}
const results = await apiBraveSearch(braveApiKey, query)
const TOTAL_SEARCH_RESULTS = await totalSearchResults()
const searchResults = results.slice(0, TOTAL_SEARCH_RESULTS)
const isSimpleMode = await getIsSimpleInternetSearch()
if (isSimpleMode) {
await getOllamaURL()
return searchResults.map((result) => {
return {
url: result.link,
content: result.content
}
})
}
const docs: Document<Record<string, any>>[] = []
try {
for (const result of searchResults) {
const loader = new PageAssistHtmlLoader({
html: "",
url: result.link
})
const documents = await loader.loadByURL()
documents.forEach((doc) => {
docs.push(doc)
})
}
} catch (error) {
console.error(error)
}
const ollamaUrl = await getOllamaURL()
const embeddingModel = await defaultEmbeddingModelForRag()
const ollamaEmbedding = await pageAssistEmbeddingModel({
model: embeddingModel || "",
baseUrl: cleanUrl(ollamaUrl)
})
const chunkSize = await defaultEmbeddingChunkSize()
const chunkOverlap = await defaultEmbeddingChunkOverlap()
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize,
chunkOverlap
})
const chunks = await textSplitter.splitDocuments(docs)
const store = new MemoryVectorStore(ollamaEmbedding)
await store.addDocuments(chunks)
const resultsWithEmbeddings = await store.similaritySearch(query, 3)
const searchResult = resultsWithEmbeddings.map((result) => {
return {
url: result.metadata.url,
content: result.pageContent
}
})
return searchResult
}
const apiBraveSearch = async (braveApiKey: string, query: string) => {
const TOTAL_SEARCH_RESULTS = await totalSearchResults()
const searchURL = `https://api.search.brave.com/res/v1/web/search?q=${query}&count=${TOTAL_SEARCH_RESULTS}`
const abortController = new AbortController()
setTimeout(() => abortController.abort(), 20000)
try {
const response = await fetch(searchURL, {
signal: abortController.signal,
headers: {
"X-Subscription-Token": braveApiKey,
Accept: "application/json",
}
})
if (!response.ok) {
return []
}
const data = await response.json() as BraveAPIResponse
return data?.web?.results.map(result => ({
title: result.title,
link: result.url,
content: result.description
}))
} catch (error) {
console.error('Brave API search failed:', error)
return []
}
}

View File

@ -6,6 +6,7 @@ import { webSogouSearch } from "./search-engines/sogou"
import { webBraveSearch } from "./search-engines/brave" import { webBraveSearch } from "./search-engines/brave"
import { getWebsiteFromQuery, processSingleWebsite } from "./website" import { getWebsiteFromQuery, processSingleWebsite } from "./website"
import { searxngSearch } from "./search-engines/searxng" import { searxngSearch } from "./search-engines/searxng"
import { braveAPISearch } from "./search-engines/brave-api"
const getHostName = (url: string) => { const getHostName = (url: string) => {
try { try {
@ -26,6 +27,8 @@ const searchWeb = (provider: string, query: string) => {
return webBraveSearch(query) return webBraveSearch(query)
case "searxng": case "searxng":
return searxngSearch(query) return searxngSearch(query)
case "brave-api":
return braveAPISearch(query)
default: default:
return webGoogleSearch(query) return webGoogleSearch(query)
} }

View File

@ -50,7 +50,7 @@ export default defineConfig({
outDir: "build", outDir: "build",
manifest: { manifest: {
version: "1.3.7", version: "1.3.8",
name: name:
process.env.TARGET === "firefox" process.env.TARGET === "firefox"
? "Page Assist - A Web UI for Local AI Models" ? "Page Assist - A Web UI for Local AI Models"