commit
						216b21de2a
					
				| @ -2,8 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| [](https://discord.gg/bu54382uBd) | [](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. | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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": "اشتراکگذاری" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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": "تاریخچه گپ، پایگاه دانش و پرامپتها را اکسپورت کنید", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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": "共有" | ||||||
|  |   } | ||||||
| } | } | ||||||
| @ -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": "チャット履歴、知識ベース、プロンプトをエクスポート", | ||||||
|  | |||||||
| @ -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": "공유" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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": "채팅 기록, 지식 베이스, 프롬프트 내보내기", | ||||||
|  | |||||||
| @ -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": "പങ്കുവയ്ക്കുക" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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": "ചാറ്റ് ചരിത്രം, അറിവ് അടിസ്ഥാനം, പ്രോംപ്റ്റുകൾ എക്സ്പോർട്ട് ചെയ്യുക", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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", | ||||||
|  | |||||||
| @ -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": "Поделиться" | ||||||
|  |     }} | ||||||
|  | |||||||
| @ -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": "Экспорт истории чата, базы знаний и подсказок", | ||||||
|  | |||||||
| @ -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" | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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", | ||||||
|  | |||||||
| @ -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": "Поділитися" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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": "Експорт історії чату, бази знань та запитів", | ||||||
|  | |||||||
| @ -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": "分享" | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -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": "导出聊天记录、知识库和提示", | ||||||
|  | |||||||
| @ -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,15 +148,6 @@ export const ShareBtn: React.FC<Props> = ({ messages, historyId }) => { | |||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |  | ||||||
|       <Tooltip title={t("share.tooltip.share")}> |  | ||||||
|         <button |  | ||||||
|           onClick={() => setOpen(true)} |  | ||||||
|           className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"> |  | ||||||
|           <Share className="w-6 h-6" /> |  | ||||||
|         </button> |  | ||||||
|       </Tooltip> |  | ||||||
| 
 |  | ||||||
|     <Modal |     <Modal | ||||||
|       title={t("share.modal.title")} |       title={t("share.modal.title")} | ||||||
|       open={open} |       open={open} | ||||||
| @ -168,24 +165,14 @@ export const ShareBtn: React.FC<Props> = ({ messages, historyId }) => { | |||||||
|         <Form.Item |         <Form.Item | ||||||
|           name="title" |           name="title" | ||||||
|           label={t("share.form.title.label")} |           label={t("share.form.title.label")} | ||||||
|             rules={[ |           rules={[{ required: true, message: t("share.form.title.required") }]}> | ||||||
|               { required: true, message: t("share.form.title.required") } |           <Input size="large" placeholder={t("share.form.title.placeholder")} /> | ||||||
|             ]}> |  | ||||||
|             <Input |  | ||||||
|               size="large" |  | ||||||
|               placeholder={t("share.form.title.placeholder")} |  | ||||||
|             /> |  | ||||||
|         </Form.Item> |         </Form.Item> | ||||||
|         <Form.Item |         <Form.Item | ||||||
|           name="name" |           name="name" | ||||||
|           label={t("share.form.name.label")} |           label={t("share.form.name.label")} | ||||||
|             rules={[ |           rules={[{ required: true, message: t("share.form.name.required") }]}> | ||||||
|               { required: true, message: t("share.form.name.required") } |           <Input size="large" placeholder={t("share.form.name.placeholder")} /> | ||||||
|             ]}> |  | ||||||
|             <Input |  | ||||||
|               size="large" |  | ||||||
|               placeholder={t("share.form.name.placeholder")} |  | ||||||
|             /> |  | ||||||
|         </Form.Item> |         </Form.Item> | ||||||
| 
 | 
 | ||||||
|         <Form.Item> |         <Form.Item> | ||||||
| @ -211,6 +198,5 @@ export const ShareBtn: React.FC<Props> = ({ messages, historyId }) => { | |||||||
|         </Form.Item> |         </Form.Item> | ||||||
|       </Form> |       </Form> | ||||||
|     </Modal> |     </Modal> | ||||||
|     </> |  | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| @ -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" | ||||||
|  | |||||||
							
								
								
									
										163
									
								
								src/components/Layouts/MoreOptions.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/components/Layouts/MoreOptions.tsx
									
									
									
									
									
										Normal 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`) | ||||||
|  |           .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} | ||||||
|  |       /> | ||||||
|  |     </> | ||||||
|  |   ) | ||||||
|  | } | ||||||
| @ -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> | ||||||
|   ) |   ) | ||||||
|  | |||||||
| @ -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")} | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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 }) | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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 } |  | ||||||
|  | |||||||
| @ -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) | ||||||
|   ]) |   ]) | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,5 +18,9 @@ export const SUPPORTED_SERACH_PROVIDERS = [ | |||||||
|     { |     { | ||||||
|         label: "Searxng", |         label: "Searxng", | ||||||
|         value: "searxng" |         value: "searxng" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |         label: "Brave Search API", | ||||||
|  |         value: "brave-api" | ||||||
|     } |     } | ||||||
| ] | ] | ||||||
							
								
								
									
										128
									
								
								src/web/search-engines/brave-api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/web/search-engines/brave-api.ts
									
									
									
									
									
										Normal 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 [] | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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) | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -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" | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user