commit
						0542b8a6d3
					
				
							
								
								
									
										13
									
								
								src/assets/locale/de/chrome.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/assets/locale/de/chrome.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| { | ||||
|     "heading": "Chrome AI konfigurieren", | ||||
|     "status": { | ||||
|         "label": "Chrome AI-Unterstützung für Page Assist aktivieren oder deaktivieren" | ||||
|     }, | ||||
|     "error": { | ||||
|         "browser_not_supported": "Diese Version von Chrome wird vom Gemini Nano-Modell nicht unterstützt. Bitte aktualisieren Sie auf Version 127 oder höher", | ||||
|         "ai_not_supported": "Die Einstellung chrome://flags/#prompt-api-for-gemini-nano ist nicht aktiviert. Bitte aktivieren Sie sie.", | ||||
|         "ai_not_ready": "Gemini Nano ist noch nicht bereit; Sie müssen die Chrome-Einstellungen überprüfen.", | ||||
|         "internal_error": "Ein interner Fehler ist aufgetreten. Bitte versuchen Sie es später erneut." | ||||
|     }, | ||||
|     "errorDescription": "Um Chrome AI zu nutzen, benötigen Sie eine Browserversion höher als 127, die derzeit in den Dev- und Canary-Kanälen verfügbar ist. Nach dem Herunterladen der unterstützten Version folgen Sie diesen Schritten:\n\n1. Gehen Sie zu `chrome://flags/#prompt-api-for-gemini-nano` und wählen Sie \"Aktivieren\".\n2. Gehen Sie zu `chrome://flags/#optimization-guide-on-device-model` und wählen Sie \"EnabledBypassPrefRequirement\".\n3. Gehen Sie zu `chrome://components`, suchen Sie nach \"Optimization Guide On Device Model\" und klicken Sie auf \"Nach Update suchen\". Dies wird das Modell herunterladen. Wenn Sie die Einstellungen nicht sehen, wiederholen Sie die Schritte 1 und 2 und starten Sie Ihren Browser neu." | ||||
| } | ||||
							
								
								
									
										100
									
								
								src/assets/locale/de/common.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/assets/locale/de/common.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| { | ||||
|     "pageAssist": "Page Assist", | ||||
|     "selectAModel": "Modell auswählen", | ||||
|     "save": "Speichern", | ||||
|     "saved": "Gespeichert", | ||||
|     "cancel": "Abbrechen", | ||||
|     "retry": "Erneut versuchen", | ||||
|     "share": { | ||||
|         "tooltip": { | ||||
|             "share": "Teilen" | ||||
|         }, | ||||
|         "modal": { | ||||
|             "title": "Link zum Chat teilen" | ||||
|         }, | ||||
|         "form": { | ||||
|             "defaultValue": { | ||||
|                 "name": "Anonym", | ||||
|                 "title": "Unbenannter Chat" | ||||
|             }, | ||||
|             "title": { | ||||
|                 "label": "Chat-Titel", | ||||
|                 "placeholder": "Chat-Titel eingeben", | ||||
|                 "required": "Chat-Titel ist erforderlich" | ||||
|             }, | ||||
|             "name": { | ||||
|                 "label": "Ihr Name", | ||||
|                 "placeholder": "Geben Sie Ihren Namen ein", | ||||
|                 "required": "Ihr Name ist erforderlich" | ||||
|             }, | ||||
|             "btn": { | ||||
|                 "save": "Link generieren", | ||||
|                 "saving": "Link wird generiert..." | ||||
|             } | ||||
|         }, | ||||
|         "notification": { | ||||
|             "successGenerate": "Link in die Zwischenablage kopiert", | ||||
|             "failGenerate": "Link konnte nicht generiert werden" | ||||
|         } | ||||
|     }, | ||||
|     "copyToClipboard": "In die Zwischenablage kopieren", | ||||
|     "webSearch": "Web durchsuchen", | ||||
|     "regenerate": "Neu generieren", | ||||
|     "edit": "Bearbeiten", | ||||
|     "saveAndSubmit": "Speichern & Absenden", | ||||
|     "editMessage": { | ||||
|         "placeholder": "Nachricht eingeben..." | ||||
|     }, | ||||
|     "submit": "Absenden", | ||||
|     "noData": "Keine Daten", | ||||
|     "noHistory": "Kein Chat-Verlauf", | ||||
|     "chatWithCurrentPage": "Mit aktueller Seite chatten", | ||||
|     "beta": "Beta", | ||||
|     "tts": "Vorlesen", | ||||
|     "currentChatModelSettings": "Aktuelle Chat-Modell-Einstellungen", | ||||
|     "modelSettings": { | ||||
|         "label": "Modell-Einstellungen", | ||||
|         "description": "Legen Sie die Modelloptionen global für alle Chats fest", | ||||
|         "form": { | ||||
|             "keepAlive": { | ||||
|                 "label": "Aktiv halten", | ||||
|                 "help": "Steuert, wie lange das Modell nach der Anfrage im Speicher geladen bleibt (Standard: 5m)", | ||||
|                 "placeholder": "Geben Sie die Dauer für Aktiv halten ein (z.B. 5m, 10m, 1h)" | ||||
|             }, | ||||
|             "temperature": { | ||||
|                 "label": "Temperatur", | ||||
|                 "placeholder": "Geben Sie den Temperaturwert ein (z.B. 0.7, 1.0)" | ||||
|             }, | ||||
|             "numCtx": { | ||||
|                 "label": "Anzahl der Kontexte", | ||||
|                 "placeholder": "Geben Sie die Anzahl der Kontexte ein (Standard: 2048)" | ||||
|             }, | ||||
|             "seed": { | ||||
|                 "label": "Seed", | ||||
|                 "placeholder": "Geben Sie den Seed-Wert ein (z.B. 1234)", | ||||
|                 "help": "Reproduzierbarkeit der Modellausgabe" | ||||
|             }, | ||||
|             "topK": { | ||||
|                 "label": "Top K", | ||||
|                 "placeholder": "Geben Sie den Top-K-Wert ein (z.B. 40, 100)" | ||||
|             }, | ||||
|             "topP": { | ||||
|                 "label": "Top P", | ||||
|                 "placeholder": "Geben Sie den Top-P-Wert ein (z.B. 0.9, 0.95)" | ||||
|             }, | ||||
|             "numGpu": { | ||||
|                 "label": "Anzahl GPUs", | ||||
|                 "placeholder": "Geben Sie die Anzahl der Ebenen ein, die an GPU(s) gesendet werden sollen" | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "Weitere Modell-Einstellungen" | ||||
|     }, | ||||
|     "copilot": { | ||||
|         "summary": "Zusammenfassen", | ||||
|         "explain": "Erklären", | ||||
|         "rephrase": "Umformulieren", | ||||
|         "translate": "Übersetzen", | ||||
|         "custom": "Benutzerdefiniert" | ||||
|     }, | ||||
|     "citations": "Zitate" | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/assets/locale/de/knowledge.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/assets/locale/de/knowledge.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| { | ||||
|     "addBtn": "Neues Wissen hinzufügen", | ||||
|     "columns": { | ||||
|         "title": "Titel", | ||||
|         "status": "Status", | ||||
|         "embeddings": "Einbettungsmodell", | ||||
|         "createdAt": "Erstellt am", | ||||
|         "action": "Aktionen" | ||||
|     }, | ||||
|     "expandedColumns": { | ||||
|         "name": "Name" | ||||
|     }, | ||||
|     "tooltip": { | ||||
|         "delete": "Löschen" | ||||
|     }, | ||||
|     "confirm": { | ||||
|         "delete": "Sind Sie sicher, dass Sie dieses Wissen löschen möchten?" | ||||
|     }, | ||||
|     "deleteSuccess": "Wissen erfolgreich gelöscht", | ||||
|     "status": { | ||||
|         "pending": "Ausstehend", | ||||
|         "finished": "Abgeschlossen", | ||||
|         "processing": "In Bearbeitung", | ||||
|         "failed": "Fehlgeschlagen" | ||||
|     }, | ||||
|     "addKnowledge": "Wissen hinzufügen", | ||||
|     "form": { | ||||
|         "title": { | ||||
|             "label": "Wissenstitel", | ||||
|             "placeholder": "Wissenstitel eingeben", | ||||
|             "required": "Wissenstitel ist erforderlich" | ||||
|         }, | ||||
|         "uploadFile": { | ||||
|             "label": "Datei hochladen", | ||||
|             "uploadText": "Ziehen Sie eine Datei hierher oder klicken Sie zum Hochladen", | ||||
|             "uploadHint": "Unterstützte Dateitypen: .pdf, .csv, .txt, .md, .docx", | ||||
|             "required": "Datei ist erforderlich" | ||||
|         }, | ||||
|         "submit": "Absenden", | ||||
|         "success": "Wissen erfolgreich hinzugefügt" | ||||
|     }, | ||||
|     "noEmbeddingModel": "Bitte fügen Sie zuerst ein Einbettungsmodell von der RAG-Einstellungsseite hinzu" | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/assets/locale/de/option.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/assets/locale/de/option.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| { | ||||
|     "newChat": "Neuer Chat", | ||||
|     "selectAPrompt": "Wähle eine Eingabeaufforderung", | ||||
|     "githubRepository": "GitHub-Repository", | ||||
|     "settings": "Einstellungen", | ||||
|     "sidebarTitle": "Chat-Verlauf", | ||||
|     "error": "Fehler", | ||||
|     "somethingWentWrong": "Etwas ist schiefgelaufen", | ||||
|     "validationSelectModel": "Bitte wähle ein Modell aus, um fortzufahren", | ||||
|     "deleteHistoryConfirmation": "Bist du sicher, dass du diesen Verlauf löschen möchtest?", | ||||
|     "editHistoryTitle": "Gib einen neuen Titel ein" | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/assets/locale/de/playground.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/assets/locale/de/playground.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| { | ||||
|     "ollamaState": { | ||||
|         "searching": "Suche nach Ihrem Ollama 🦙", | ||||
|         "running": "Ollama läuft 🦙", | ||||
|         "notRunning": "Verbindung zu Ollama nicht möglich 🦙", | ||||
|         "connectionError": "Es scheint, dass Sie ein Verbindungsproblem haben. Bitte beachten Sie diese <anchor>Dokumentation</anchor> zur Fehlerbehebung." | ||||
|     }, | ||||
|     "formError": { | ||||
|         "noModel": "Bitte wählen Sie ein Modell aus", | ||||
|         "noEmbeddingModel": "Bitte legen Sie ein Embedding-Modell auf der Seite Einstellungen > RAG fest" | ||||
|     }, | ||||
|     "form": { | ||||
|         "textarea": { | ||||
|             "placeholder": "Nachricht eingeben..." | ||||
|         }, | ||||
|         "webSearch": { | ||||
|             "on": "An", | ||||
|             "off": "Aus" | ||||
|         } | ||||
|     }, | ||||
|     "tooltip": { | ||||
|         "searchInternet": "Internet durchsuchen", | ||||
|         "speechToText": "Sprache zu Text", | ||||
|         "uploadImage": "Bild hochladen", | ||||
|         "stopStreaming": "Streaming stoppen", | ||||
|         "knowledge": "Wissen" | ||||
|     }, | ||||
|     "sendWhenEnter": "Senden bei Drücken der Eingabetaste" | ||||
| } | ||||
							
								
								
									
										341
									
								
								src/assets/locale/de/settings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								src/assets/locale/de/settings.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,341 @@ | ||||
| { | ||||
|   "generalSettings": { | ||||
|     "title": "Allgemeine Einstellungen", | ||||
|     "settings": { | ||||
|       "heading": "Web-UI-Einstellungen", | ||||
|       "speechRecognitionLang": { | ||||
|         "label": "Spracherkennungssprache", | ||||
|         "placeholder": "Sprache auswählen" | ||||
|       }, | ||||
|       "language": { | ||||
|         "label": "Sprache", | ||||
|         "placeholder": "Sprache auswählen" | ||||
|       }, | ||||
|       "darkMode": { | ||||
|         "label": "Design ändern", | ||||
|         "options": { | ||||
|           "light": "Hell", | ||||
|           "dark": "Dunkel" | ||||
|         } | ||||
|       }, | ||||
|       "copilotResumeLastChat": { | ||||
|         "label": "Letzten Chat beim Öffnen des Seitenpanels fortsetzen (Copilot)" | ||||
|       }, | ||||
|       "hideCurrentChatModelSettings": { | ||||
|         "label": "Aktuelle Chat-Modell-Einstellungen ausblenden" | ||||
|       }, | ||||
|       "restoreLastChatModel": { | ||||
|         "label": "Zuletzt verwendetes Modell für vorherige Chats wiederherstellen" | ||||
|       }, | ||||
|       "sendNotificationAfterIndexing": { | ||||
|         "label": "Benachrichtigung nach Abschluss der Wissensbasis-Verarbeitung senden" | ||||
|       }, | ||||
|       "generateTitle" :{ | ||||
|         "label": "Titel mit KI generieren" | ||||
|       } | ||||
|     }, | ||||
|     "sidepanelRag": { | ||||
|       "heading": "Copilot Chat mit Website-Einstellungen", | ||||
|       "ragEnabled": { | ||||
|         "label": "Chat mit Website unter Verwendung von Vektor-Embeddings" | ||||
|       }, | ||||
|       "maxWebsiteContext": { | ||||
|         "label": "Normaler Modus Website-Inhaltsgröße", | ||||
|         "placeholder": "Inhaltsgröße (Standard 4028)" | ||||
|       } | ||||
|     }, | ||||
|     "webSearch": { | ||||
|       "heading": "Websuche verwalten", | ||||
|       "searchMode": { | ||||
|         "label": "Einfache Internetsuche durchführen" | ||||
|       }, | ||||
|       "provider": { | ||||
|         "label": "Suchmaschine", | ||||
|         "placeholder": "Suchmaschine auswählen" | ||||
|       }, | ||||
|       "totalSearchResults": { | ||||
|         "label": "Gesamtanzahl der Suchergebnisse", | ||||
|         "placeholder": "Gesamtanzahl der Suchergebnisse eingeben" | ||||
|       }, | ||||
|       "visitSpecificWebsite": { | ||||
|         "label": "Die in der Nachricht erwähnte Website besuchen" | ||||
|       } | ||||
|     }, | ||||
|     "system": { | ||||
|       "heading": "Systemeinstellungen", | ||||
|       "deleteChatHistory": { | ||||
|         "label": "Chatverlauf löschen", | ||||
|         "button": "Löschen", | ||||
|         "confirm": "Sind Sie sicher, dass Sie Ihren Chatverlauf löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden." | ||||
|       }, | ||||
|       "export": { | ||||
|         "label": "Chatverlauf, Wissensbasis und Prompts exportieren", | ||||
|         "button": "Daten exportieren", | ||||
|         "success": "Export erfolgreich" | ||||
|       }, | ||||
|       "import": { | ||||
|         "label": "Chatverlauf, Wissensbasis und Prompts importieren", | ||||
|         "button": "Daten importieren", | ||||
|         "success": "Import erfolgreich", | ||||
|         "error": "Importfehler" | ||||
|       } | ||||
|     }, | ||||
|     "tts": { | ||||
|       "heading": "Text-zu-Sprache-Einstellungen", | ||||
|       "ttsEnabled": { | ||||
|         "label": "Text-zu-Sprache aktivieren" | ||||
|       }, | ||||
|       "ttsProvider": { | ||||
|         "label": "Text-zu-Sprache-Anbieter", | ||||
|         "placeholder": "Anbieter auswählen" | ||||
|       }, | ||||
|       "ttsVoice": { | ||||
|         "label": "Text-zu-Sprache-Stimme", | ||||
|         "placeholder": "Stimme auswählen" | ||||
|       }, | ||||
|       "ssmlEnabled": { | ||||
|         "label": "SSML (Speech Synthesis Markup Language) aktivieren" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "manageModels": { | ||||
|     "title": "Modelle verwalten", | ||||
|     "addBtn": "Neues Modell hinzufügen", | ||||
|     "columns": { | ||||
|       "name": "Name", | ||||
|       "digest": "Digest", | ||||
|       "modifiedAt": "Zuletzt geändert", | ||||
|       "size": "Größe", | ||||
|       "actions": "Aktionen" | ||||
|     }, | ||||
|     "expandedColumns": { | ||||
|       "parentModel": "Übergeordnetes Modell", | ||||
|       "format": "Format", | ||||
|       "family": "Familie", | ||||
|       "parameterSize": "Parametergröße", | ||||
|       "quantizationLevel": "Quantisierungsstufe" | ||||
|     }, | ||||
|     "tooltip": { | ||||
|       "delete": "Modell löschen", | ||||
|       "repull": "Modell erneut herunterladen" | ||||
|     }, | ||||
|     "confirm": { | ||||
|       "delete": "Sind Sie sicher, dass Sie dieses Modell löschen möchten?", | ||||
|       "repull": "Sind Sie sicher, dass Sie dieses Modell erneut herunterladen möchten?" | ||||
|     }, | ||||
|     "modal": { | ||||
|       "title": "Neues Modell hinzufügen", | ||||
|       "placeholder": "Modellnamen eingeben", | ||||
|       "pull": "Modell herunterladen" | ||||
|     }, | ||||
|     "notification": { | ||||
|       "pullModel": "Modell wird heruntergeladen", | ||||
|       "pullModelDescription": "Das Modell {{modelName}} wird heruntergeladen. Weitere Details finden Sie im Erweiterungssymbol.", | ||||
|       "success": "Erfolgreich", | ||||
|       "error": "Fehler", | ||||
|       "successDescription": "Das Modell wurde erfolgreich heruntergeladen", | ||||
|       "successDeleteDescription": "Das Modell wurde erfolgreich gelöscht", | ||||
|       "someError": "Etwas ist schiefgelaufen. Bitte versuchen Sie es später erneut" | ||||
|     } | ||||
|   }, | ||||
|   "managePrompts": { | ||||
|     "title": "Prompts verwalten", | ||||
|     "addBtn": "Neuen Prompt hinzufügen", | ||||
|     "option1": "Normal", | ||||
|     "option2": "RAG", | ||||
|     "questionPrompt": "Frage-Prompt", | ||||
|     "segmented": { | ||||
|       "custom": "Benutzerdefinierte Prompts", | ||||
|       "copilot": "Copilot-Prompts" | ||||
|     }, | ||||
|     "columns": { | ||||
|       "title": "Titel", | ||||
|       "prompt": "Prompt", | ||||
|       "type": "Prompt-Typ", | ||||
|       "actions": "Aktionen" | ||||
|     }, | ||||
|     "systemPrompt": "System-Prompt", | ||||
|     "quickPrompt": "Schnell-Prompt", | ||||
|     "tooltip": { | ||||
|       "delete": "Prompt löschen", | ||||
|       "edit": "Prompt bearbeiten" | ||||
|     }, | ||||
|     "confirm": { | ||||
|       "delete": "Sind Sie sicher, dass Sie diesen Prompt löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden." | ||||
|     }, | ||||
|     "modal": { | ||||
|       "addTitle": "Neuen Prompt hinzufügen", | ||||
|       "editTitle": "Prompt bearbeiten" | ||||
|     }, | ||||
|     "form": { | ||||
|       "title": { | ||||
|         "label": "Titel", | ||||
|         "placeholder": "Mein toller Prompt", | ||||
|         "required": "Bitte geben Sie einen Titel ein" | ||||
|       }, | ||||
|       "prompt": { | ||||
|         "label": "Prompt", | ||||
|         "placeholder": "Prompt eingeben", | ||||
|         "required": "Bitte geben Sie einen Prompt ein", | ||||
|         "help": "Sie können {key} als Variable in Ihrem Prompt verwenden.", | ||||
|         "missingTextPlaceholder": "Die Variable {text} fehlt im Prompt. Bitte fügen Sie sie hinzu." | ||||
|       }, | ||||
|       "isSystem": { | ||||
|         "label": "Ist System-Prompt" | ||||
|       }, | ||||
|       "btnSave": { | ||||
|         "saving": "Prompt wird hinzugefügt...", | ||||
|         "save": "Prompt hinzufügen" | ||||
|       }, | ||||
|       "btnEdit": { | ||||
|         "saving": "Prompt wird aktualisiert...", | ||||
|         "save": "Prompt aktualisieren" | ||||
|       } | ||||
|     }, | ||||
|     "notification": { | ||||
|       "addSuccess": "Prompt hinzugefügt", | ||||
|       "addSuccessDesc": "Prompt wurde erfolgreich hinzugefügt", | ||||
|       "error": "Fehler", | ||||
|       "someError": "Etwas ist schiefgelaufen. Bitte versuchen Sie es später erneut", | ||||
|       "updatedSuccess": "Prompt aktualisiert", | ||||
|       "updatedSuccessDesc": "Prompt wurde erfolgreich aktualisiert", | ||||
|       "deletedSuccess": "Prompt gelöscht", | ||||
|       "deletedSuccessDesc": "Prompt wurde erfolgreich gelöscht" | ||||
|     } | ||||
|   }, | ||||
|   "manageShare": { | ||||
|     "title": "Freigabe verwalten", | ||||
|     "heading": "Seiten-Freigabe-URL konfigurieren", | ||||
|     "form": { | ||||
|       "url": { | ||||
|         "label": "Seiten-Freigabe-URL", | ||||
|         "placeholder": "Seiten-Freigabe-URL eingeben", | ||||
|         "required": "Bitte geben Sie Ihre Seiten-Freigabe-URL ein!", | ||||
|         "help": "Aus Datenschutzgründen können Sie die Seitenfreigabe selbst hosten und die URL hier angeben. <anchor>Mehr erfahren</anchor>." | ||||
|       } | ||||
|     }, | ||||
|     "webshare": { | ||||
|       "heading": "Web-Freigabe", | ||||
|       "columns": { | ||||
|         "title": "Titel", | ||||
|         "url": "URL", | ||||
|         "actions": "Aktionen" | ||||
|       }, | ||||
|       "tooltip": { | ||||
|         "delete": "Freigabe löschen" | ||||
|       }, | ||||
|       "confirm": { | ||||
|         "delete": "Sind Sie sicher, dass Sie diese Freigabe löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden." | ||||
|       }, | ||||
|       "label": "Seitenfreigabe verwalten", | ||||
|       "description": "Seitenfreigabe-Funktion aktivieren oder deaktivieren" | ||||
|     }, | ||||
|     "notification": { | ||||
|       "pageShareSuccess": "Seiten-Freigabe-URL erfolgreich aktualisiert", | ||||
|       "someError": "Etwas ist schiefgelaufen. Bitte versuchen Sie es später erneut", | ||||
|       "webShareDeleteSuccess": "Web-Freigabe erfolgreich gelöscht" | ||||
|     } | ||||
|   }, | ||||
|   "ollamaSettings": { | ||||
|     "title": "Ollama-Einstellungen", | ||||
|     "heading": "Ollama konfigurieren", | ||||
|     "settings": { | ||||
|       "ollamaUrl": { | ||||
|         "label": "Ollama-URL", | ||||
|         "placeholder": "Ollama-URL eingeben" | ||||
|       }, | ||||
|       "advanced": { | ||||
|         "label": "Erweiterte Ollama-URL-Konfiguration", | ||||
|         "urlRewriteEnabled": { | ||||
|           "label": "Benutzerdefinierte Ursprungs-URL aktivieren oder deaktivieren" | ||||
|         }, | ||||
|         "rewriteUrl": { | ||||
|           "label": "Benutzerdefinierte Ursprungs-URL", | ||||
|           "placeholder": "Benutzerdefinierte Ursprungs-URL eingeben" | ||||
|         }, | ||||
|         "headers": { | ||||
|           "label": "Benutzerdefinierte Header", | ||||
|           "add": "Header hinzufügen", | ||||
|           "key": { | ||||
|             "label": "Header-Schlüssel", | ||||
|             "placeholder": "Autorisierung" | ||||
|           }, | ||||
|           "value": { | ||||
|             "label": "Header-Wert", | ||||
|             "placeholder": "Bearer-Token" | ||||
|           } | ||||
|         }, | ||||
|         "help": "Wenn Sie Verbindungsprobleme mit Ollama auf Page Assist haben, können Sie eine benutzerdefinierte Ursprungs-URL konfigurieren. Um mehr über die Konfiguration zu erfahren, <anchor>klicken Sie hier</anchor>." | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "manageSearch": { | ||||
|     "title": "Web-Suche verwalten", | ||||
|     "heading": "Web-Suche konfigurieren" | ||||
|   }, | ||||
|   "about": { | ||||
|     "title": "Über", | ||||
|     "heading": "Über", | ||||
|     "chromeVersion": "Page Assist Version", | ||||
|     "ollamaVersion": "Ollama Version", | ||||
|     "support": "Sie können das Page Assist-Projekt durch Spenden oder Sponsoring über die folgenden Plattformen unterstützen:", | ||||
|     "koFi": "Unterstützen Sie uns auf Ko-fi", | ||||
|     "githubSponsor": "Sponsern Sie uns auf GitHub", | ||||
|     "githubRepo": "GitHub-Repository" | ||||
|   }, | ||||
|   "manageKnowledge": { | ||||
|     "title": "Wissen verwalten", | ||||
|     "heading": "Wissensbasis konfigurieren" | ||||
|   }, | ||||
|   "rag": { | ||||
|     "title": "RAG-Einstellungen", | ||||
|     "ragSettings": { | ||||
|       "label": "RAG-Einstellungen", | ||||
|       "model": { | ||||
|         "label": "Embedding-Modell", | ||||
|         "required": "Bitte wählen Sie ein Modell aus", | ||||
|         "help": "Es wird dringend empfohlen, Embedding-Modelle wie `nomic-embed-text` zu verwenden.", | ||||
|         "placeholder": "Wählen Sie ein Modell aus" | ||||
|       }, | ||||
|       "chunkSize": { | ||||
|         "label": "Chunk-Größe", | ||||
|         "placeholder": "Chunk-Größe eingeben", | ||||
|         "required": "Bitte geben Sie eine Chunk-Größe ein" | ||||
|       }, | ||||
|       "chunkOverlap": { | ||||
|         "label": "Chunk-Überlappung", | ||||
|         "placeholder": "Chunk-Überlappung eingeben", | ||||
|         "required": "Bitte geben Sie eine Chunk-Überlappung ein" | ||||
|       }, | ||||
|       "totalFilePerKB": { | ||||
|         "label": "Standard-Datei-Upload-Limit für die Wissensbasis", | ||||
|         "placeholder": "Geben Sie das Standard-Datei-Upload-Limit ein (z.B. 10)", | ||||
|         "required": "Bitte geben Sie das Standard-Datei-Upload-Limit ein" | ||||
|       }, | ||||
|       "noOfRetrievedDocs": { | ||||
|         "label": "Anzahl der abgerufenen Dokumente", | ||||
|         "placeholder": "Anzahl der abgerufenen Dokumente eingeben", | ||||
|         "required": "Bitte geben Sie die Anzahl der abgerufenen Dokumente ein" | ||||
|       } | ||||
|     }, | ||||
|     "prompt": { | ||||
|       "label": "RAG-Prompt konfigurieren", | ||||
|       "option1": "Normal", | ||||
|       "option2": "Web", | ||||
|       "alert": "Die Konfiguration des System-Prompts hier ist veraltet. Bitte verwenden Sie den Abschnitt 'Prompts verwalten', um Prompts hinzuzufügen oder zu bearbeiten. Dieser Abschnitt wird in einer zukünftigen Version entfernt", | ||||
|       "systemPrompt": "System-Prompt", | ||||
|       "systemPromptPlaceholder": "System-Prompt eingeben", | ||||
|       "webSearchPrompt": "Web-Suche-Prompt", | ||||
|       "webSearchPromptHelp": "Entfernen Sie `{search_results}` nicht aus dem Prompt.", | ||||
|       "webSearchPromptError": "Bitte geben Sie einen Web-Suche-Prompt ein", | ||||
|       "webSearchPromptPlaceholder": "Web-Suche-Prompt eingeben", | ||||
|       "webSearchFollowUpPrompt": "Web-Suche-Folgeprompt", | ||||
|       "webSearchFollowUpPromptHelp": "Entfernen Sie `{chat_history}` und `{question}` nicht aus dem Prompt.", | ||||
|       "webSearchFollowUpPromptError": "Bitte geben Sie Ihren Web-Suche-Folgeprompt ein!", | ||||
|       "webSearchFollowUpPromptPlaceholder": "Ihr Web-Suche-Folgeprompt" | ||||
|     } | ||||
|   }, | ||||
|   "chromeAiSettings": { | ||||
|     "title": "Chrome AI-Einstellungen" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/assets/locale/de/sidepanel.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/assets/locale/de/sidepanel.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| { | ||||
|     "tooltip": { | ||||
|         "embed": "Es kann einige Minuten dauern, die Seite einzubetten. Bitte warten Sie...", | ||||
|         "clear": "Chatverlauf löschen", | ||||
|         "history": "Chatverlauf" | ||||
|     } | ||||
| } | ||||
| @ -95,5 +95,6 @@ | ||||
|         "rephrase": "Rephrase", | ||||
|         "translate": "Translate", | ||||
|         "custom": "Custom" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "Citations" | ||||
| } | ||||
| @ -94,5 +94,6 @@ | ||||
|         "explain": "Explicar", | ||||
|         "rephrase": "Reformular", | ||||
|         "translate": "Traducir" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "Citas" | ||||
| } | ||||
| @ -88,5 +88,6 @@ | ||||
|             } | ||||
|         }, | ||||
|         "advanced": "تنظیمات بیشتر مدل" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "منابع" | ||||
| } | ||||
| @ -94,5 +94,6 @@ | ||||
|         "explain": "Expliquer", | ||||
|         "rephrase": "Reformuler", | ||||
|         "translate": "Traduire" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "Citations" | ||||
| } | ||||
| @ -93,5 +93,6 @@ | ||||
|         "explain": "Spiegare", | ||||
|         "rephrase": "Riformulare", | ||||
|         "translate": "Tradurre" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "Citazioni" | ||||
| } | ||||
| @ -94,5 +94,6 @@ | ||||
|     "explain": "説明", | ||||
|     "rephrase": "言い換え", | ||||
|     "translate": "翻訳" | ||||
|   } | ||||
|   }, | ||||
|   "citations": "引用" | ||||
| } | ||||
| @ -93,5 +93,6 @@ | ||||
|         "explain": "വിശദീകരിക്കുക", | ||||
|         "rephrase": "പുനഃരൂപീകരിക്കുക", | ||||
|         "translate": "വിവർത്തനം ചെയ്യുക" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "ഉദ്ധരണികൾ" | ||||
| } | ||||
| @ -94,5 +94,6 @@ | ||||
|     "explain": "Explicar", | ||||
|     "rephrase": "Reformular", | ||||
|     "translate": "Traduzir" | ||||
|   } | ||||
|   }, | ||||
|   "citations": "Citações" | ||||
| } | ||||
| @ -93,5 +93,6 @@ | ||||
|         "explain": "Объяснить", | ||||
|         "rephrase": "Перефразировать", | ||||
|         "translate": "Перевести" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "Цитаты" | ||||
| } | ||||
| @ -94,5 +94,6 @@ | ||||
|         "explain": "解释", | ||||
|         "rephrase": "重述", | ||||
|         "translate": "翻译" | ||||
|     } | ||||
|     }, | ||||
|     "citations": "引用" | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| import Markdown from "../../Common/Markdown" | ||||
| import React from "react" | ||||
| import { Tag, Image, Tooltip } from "antd" | ||||
| import { Tag, Image, Tooltip, Collapse } from "antd" | ||||
| import { WebSearch } from "./WebSearch" | ||||
| import { | ||||
|   CheckIcon, | ||||
| @ -127,15 +127,31 @@ export const PlaygroundMessage = (props: Props) => { | ||||
|               )} | ||||
| 
 | ||||
|             {props.isBot && props?.sources && props?.sources.length > 0 && ( | ||||
|               <div className="mb-3 flex flex-wrap gap-2"> | ||||
|                 {props?.sources?.map((source, index) => ( | ||||
|                   <MessageSource | ||||
|                     onSourceClick={props.onSourceClick} | ||||
|                     key={index} | ||||
|                     source={source} | ||||
|                   /> | ||||
|                 ))} | ||||
|               </div> | ||||
|               <Collapse | ||||
|                 className="mt-6" | ||||
|                 ghost | ||||
|                 items={[ | ||||
|                   { | ||||
|                     key: "1", | ||||
|                     label: ( | ||||
|                       <div className="italic text-gray-500 dark:text-gray-400"> | ||||
|                         {t('citations')} | ||||
|                       </div> | ||||
|                     ), | ||||
|                     children: ( | ||||
|                       <div className="mb-3 flex flex-wrap gap-2"> | ||||
|                         {props?.sources?.map((source, index) => ( | ||||
|                           <MessageSource | ||||
|                             onSourceClick={props.onSourceClick} | ||||
|                             key={index} | ||||
|                             source={source} | ||||
|                           /> | ||||
|                         ))} | ||||
|                       </div> | ||||
|                     ) | ||||
|                   } | ||||
|                 ]} | ||||
|               /> | ||||
|             )} | ||||
|             {!props.isProcessing && !editMode && ( | ||||
|               <div | ||||
|  | ||||
| @ -9,6 +9,7 @@ import { getPageShareUrl } from "~/services/ollama" | ||||
| import { cleanUrl } from "~/libs/clean-url" | ||||
| import { getUserId, saveWebshare } from "@/db" | ||||
| import { useTranslation } from "react-i18next" | ||||
| import fetcher from "@/libs/fetcher" | ||||
| 
 | ||||
| type Props = { | ||||
|   messages: Message[] | ||||
| @ -94,7 +95,7 @@ export const ShareBtn: React.FC<Props> = ({ messages }) => { | ||||
|     const chat = reformatMessages(messages, values.name) | ||||
|     const title = values.title | ||||
|     const url = await getPageShareUrl() | ||||
|     const res = await fetch(`${cleanUrl(url)}/api/v1/share/create`, { | ||||
|     const res = await fetcher(`${cleanUrl(url)}/api/v1/share/create`, { | ||||
|       method: "POST", | ||||
|       headers: { | ||||
|         "Content-Type": "application/json" | ||||
|  | ||||
| @ -14,6 +14,7 @@ import { useTranslation } from "react-i18next" | ||||
| import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect" | ||||
| import { useSpeechRecognition } from "@/hooks/useSpeechRecognition" | ||||
| import { PiGlobe } from "react-icons/pi" | ||||
| import { handleChatInputKeyDown } from "@/utils/key-down" | ||||
| 
 | ||||
| type Props = { | ||||
|   dropedFile: File | undefined | ||||
| @ -144,13 +145,16 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { | ||||
|   }) | ||||
| 
 | ||||
|   const handleKeyDown = (e: React.KeyboardEvent) => { | ||||
|     if (e.key === "Process" || e.key === "229") return | ||||
|     if (import.meta.env.BROWSER !== "firefox") { | ||||
|       if (e.key === "Process" || e.key === "229") return | ||||
|     } | ||||
|     if ( | ||||
|       !typing && | ||||
|       e.key === "Enter" && | ||||
|       !e.shiftKey && | ||||
|       !isSending && | ||||
|       sendWhenEnter | ||||
|       handleChatInputKeyDown({ | ||||
|         e, | ||||
|         sendWhenEnter, | ||||
|         typing, | ||||
|         isSending | ||||
|       }) | ||||
|     ) { | ||||
|       e.preventDefault() | ||||
|       stopListening() | ||||
| @ -244,8 +248,16 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { | ||||
|             /> | ||||
|             <div className="w-full border-x border-t flex flex-col dark:border-gray-600 rounded-t-xl p-2"> | ||||
|               <textarea | ||||
|                 onCompositionStart={() => setTyping(true)} | ||||
|                 onCompositionEnd={() => setTyping(false)} | ||||
|                 onCompositionStart={() => { | ||||
|                   if (import.meta.env.BROWSER !== "firefox") { | ||||
|                     setTyping(true) | ||||
|                   } | ||||
|                 }} | ||||
|                 onCompositionEnd={() => { | ||||
|                   if (import.meta.env.BROWSER !== "firefox") { | ||||
|                     setTyping(false) | ||||
|                   } | ||||
|                 }} | ||||
|                 onKeyDown={(e) => handleKeyDown(e)} | ||||
|                 ref={textareaRef} | ||||
|                 className="px-2 py-2 w-full resize-none bg-transparent focus-within:outline-none focus:ring-0 focus-visible:ring-0 ring-0 dark:ring-0 border-0 dark:text-gray-100" | ||||
|  | ||||
| @ -4,6 +4,7 @@ import { useQuery } from "@tanstack/react-query" | ||||
| import { Skeleton } from "antd" | ||||
| import { cleanUrl } from "@/libs/clean-url" | ||||
| import { Descriptions } from "antd" | ||||
| import fetcher from "@/libs/fetcher" | ||||
| 
 | ||||
| export const AboutApp = () => { | ||||
|   const { t } = useTranslation("settings") | ||||
| @ -14,7 +15,7 @@ export const AboutApp = () => { | ||||
|       const chromeVersion = browser.runtime.getManifest().version | ||||
|       try { | ||||
|         const url = await getOllamaURL() | ||||
|         const req = await fetch(`${cleanUrl(url)}/api/version`) | ||||
|         const req = await fetcher(`${cleanUrl(url)}/api/version`) | ||||
| 
 | ||||
|         if (!req.ok) { | ||||
|           return { | ||||
| @ -75,11 +76,11 @@ export const AboutApp = () => { | ||||
|                 label: "X (formerly Twitter)", | ||||
|                 children: ( | ||||
|                   <a | ||||
|                     href="https://twitter.com/n4ze3m" | ||||
|                     href="https://twitter.com/page_assist" | ||||
|                     target="_blank" | ||||
|                     rel="noreferrer" | ||||
|                     className="text-blue-500 dark:text-blue-400"> | ||||
|                     @n4ze3m | ||||
|                     @page_assist | ||||
|                   </a> | ||||
|                 ) | ||||
|               } | ||||
|  | ||||
| @ -16,9 +16,13 @@ import { | ||||
| import { useStorage } from "@plasmohq/storage/hook" | ||||
| 
 | ||||
| export const GeneralSettings = () => { | ||||
|   const { clearChat, speechToTextLanguage, setSpeechToTextLanguage } = | ||||
|   const { clearChat } = | ||||
|     useMessageOption() | ||||
| 
 | ||||
|   const [ speechToTextLanguage, setSpeechToTextLanguage ] = useStorage( | ||||
|       "speechToTextLanguage", | ||||
|       "en-US" | ||||
|     ) | ||||
|   const [copilotResumeLastChat, setCopilotResumeLastChat] = useStorage( | ||||
|     "copilotResumeLastChat", | ||||
|     false | ||||
|  | ||||
| @ -7,6 +7,7 @@ import { deleteWebshare, getAllWebshares, getUserId } from "@/db" | ||||
| import { getPageShareUrl, setPageShareUrl } from "~/services/ollama" | ||||
| import { verifyPageShareURL } from "~/utils/verify-page-share" | ||||
| import { useStorage } from "@plasmohq/storage/hook" | ||||
| import fetcher from "@/libs/fetcher" | ||||
| 
 | ||||
| export const OptionShareBody = () => { | ||||
|   const queryClient = useQueryClient() | ||||
| @ -48,7 +49,7 @@ export const OptionShareBody = () => { | ||||
|     api_url: string | ||||
|   }) => { | ||||
|     const owner_id = await getUserId() | ||||
|     const res = await fetch(`${api_url}/api/v1/share/delete`, { | ||||
|     const res = await fetcher(`${api_url}/api/v1/share/delete`, { | ||||
|       method: "POST", | ||||
|       headers: { | ||||
|         "Content-Type": "application/json" | ||||
|  | ||||
| @ -12,6 +12,7 @@ import { useTranslation } from "react-i18next" | ||||
| import { ModelSelect } from "@/components/Common/ModelSelect" | ||||
| import { useSpeechRecognition } from "@/hooks/useSpeechRecognition" | ||||
| import { PiGlobeX, PiGlobe } from "react-icons/pi" | ||||
| import { handleChatInputKeyDown } from "@/utils/key-down" | ||||
| 
 | ||||
| type Props = { | ||||
|   dropedFile: File | undefined | ||||
| @ -66,11 +67,12 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | ||||
|   const handleKeyDown = (e: React.KeyboardEvent) => { | ||||
|     if (e.key === "Process" || e.key === "229") return | ||||
|     if ( | ||||
|       e.key === "Enter" && | ||||
|       !e.shiftKey && | ||||
|       !isSending && | ||||
|       sendWhenEnter && | ||||
|       !typing | ||||
|       handleChatInputKeyDown({ | ||||
|         e, | ||||
|         sendWhenEnter, | ||||
|         typing, | ||||
|         isSending | ||||
|       }) | ||||
|     ) { | ||||
|       e.preventDefault() | ||||
|       form.onSubmit(async (value) => { | ||||
| @ -245,8 +247,16 @@ export const SidepanelForm = ({ dropedFile }: Props) => { | ||||
|                 rows={1} | ||||
|                 style={{ minHeight: "60px" }} | ||||
|                 tabIndex={0} | ||||
|                 onCompositionStart={() => setTyping(true)} | ||||
|                 onCompositionEnd={() => setTyping(false)} | ||||
|                 onCompositionStart={() => { | ||||
|                   if (import.meta.env.BROWSER !== "firefox") { | ||||
|                     setTyping(true) | ||||
|                   } | ||||
|                 }} | ||||
|                 onCompositionEnd={() => { | ||||
|                   if (import.meta.env.BROWSER !== "firefox") { | ||||
|                     setTyping(false) | ||||
|                   } | ||||
|                 }} | ||||
|                 placeholder={t("form.textarea.placeholder")} | ||||
|                 {...form.getInputProps("message")} | ||||
|               /> | ||||
|  | ||||
| @ -53,7 +53,10 @@ export const SettingsBody = () => { | ||||
|   const [hideCurrentChatModelSettings, setHideCurrentChatModelSettings] = | ||||
|     useStorage("hideCurrentChatModelSettings", false) | ||||
| 
 | ||||
|   const { speechToTextLanguage, setSpeechToTextLanguage } = useMessage() | ||||
|   const [ speechToTextLanguage, setSpeechToTextLanguage ] = useStorage( | ||||
|     "speechToTextLanguage", | ||||
|     "en-US" | ||||
|   ) | ||||
|   const { mode, toggleDarkMode } = useDarkMode() | ||||
| 
 | ||||
|   const { changeLocale, locale, supportLanguage } = useI18n() | ||||
|  | ||||
| @ -74,8 +74,6 @@ export const useMessage = () => { | ||||
|     setChatMode, | ||||
|     setIsEmbedding, | ||||
|     isEmbedding, | ||||
|     speechToTextLanguage, | ||||
|     setSpeechToTextLanguage, | ||||
|     currentURL, | ||||
|     setCurrentURL | ||||
|   } = useStoreMessage() | ||||
| @ -1230,8 +1228,6 @@ export const useMessage = () => { | ||||
|     chatMode, | ||||
|     setChatMode, | ||||
|     isEmbedding, | ||||
|     speechToTextLanguage, | ||||
|     setSpeechToTextLanguage, | ||||
|     regenerateLastMessage, | ||||
|     webSearch, | ||||
|     setWebSearch, | ||||
|  | ||||
| @ -10,6 +10,7 @@ import { ja } from "./lang/ja"; | ||||
| import { it } from "./lang/it"; | ||||
| import { es } from "./lang/es"; | ||||
| import { fa } from "./lang/fa"; | ||||
| import { de } from "./lang/de"; | ||||
| import LanguageDetector from 'i18next-browser-languagedetector'; | ||||
| 
 | ||||
| i18n | ||||
| @ -30,7 +31,8 @@ i18n | ||||
|             ja: ja, | ||||
|             "ja-JP": ja, | ||||
|             fa: fa, | ||||
|             "fa-IR": fa | ||||
|             "fa-IR": fa, | ||||
|             de: de | ||||
|         }, | ||||
|         fallbackLng: "en", | ||||
|         lng: localStorage.getItem("i18nextLng") || "en", | ||||
|  | ||||
							
								
								
									
										17
									
								
								src/i18n/lang/de.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/i18n/lang/de.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import option from "@/assets/locale/de/option.json"; | ||||
| import playground from "@/assets/locale/de/playground.json"; | ||||
| import common from "@/assets/locale/de/common.json"; | ||||
| import sidepanel from "@/assets/locale/de/sidepanel.json"; | ||||
| import settings from "@/assets/locale/de/settings.json"; | ||||
| import knowledge from "@/assets/locale/de/knowledge.json"; | ||||
| import chrome from "@/assets/locale/de/chrome.json"; | ||||
| 
 | ||||
| export const de = { | ||||
|     option, | ||||
|     playground, | ||||
|     common, | ||||
|     sidepanel, | ||||
|     settings, | ||||
|     knowledge, | ||||
|     chrome | ||||
| } | ||||
| @ -39,5 +39,9 @@ export const supportLanguage = [ | ||||
|     { | ||||
|         label: "فارسی", | ||||
|         value: "fa" | ||||
|     }, | ||||
|     { | ||||
|         label: "Deutsch", | ||||
|         value: "de" | ||||
|     } | ||||
| ] | ||||
|  | ||||
							
								
								
									
										14
									
								
								src/libs/fetcher.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/libs/fetcher.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| import { getCustomOllamaHeaders } from "@/services/app" | ||||
| 
 | ||||
| 
 | ||||
| const fetcher = async (input: string | URL | globalThis.Request, init?: RequestInit) : Promise<Response> => { | ||||
|     const update = {...init} || {} | ||||
|     const customHeaders = await getCustomOllamaHeaders() | ||||
|     update.headers = { | ||||
|         ...customHeaders, | ||||
|         ...update?.headers | ||||
|     } | ||||
|     return fetch(input, update) | ||||
| } | ||||
| 
 | ||||
| export default fetcher | ||||
| @ -10,19 +10,8 @@ import { | ||||
| import { BaseMessage, AIMessageChunk } from "@langchain/core/messages" | ||||
| import { ChatGenerationChunk } from "@langchain/core/outputs" | ||||
| import { IterableReadableStream } from "@langchain/core/utils/stream" | ||||
| import { AITextSession, checkChromeAIAvailability, createAITextSession } from "./utils/chrome" | ||||
| 
 | ||||
| export interface AI { | ||||
|   canCreateTextSession(): Promise<AIModelAvailability> | ||||
|   createTextSession(options?: AITextSessionOptions): Promise<AITextSession> | ||||
|   defaultTextSessionOptions(): Promise<AITextSessionOptions> | ||||
| } | ||||
| 
 | ||||
| export interface AITextSession { | ||||
|   prompt(input: string): Promise<string> | ||||
|   promptStreaming(input: string): ReadableStream | ||||
|   destroy(): void | ||||
|   clone(): AITextSession | ||||
| } | ||||
| 
 | ||||
| export interface AITextSessionOptions { | ||||
|   topK: number | ||||
| @ -44,16 +33,12 @@ export interface ChromeAIInputs extends BaseChatModelParams { | ||||
|   promptFormatter?: (messages: BaseMessage[]) => string | ||||
| } | ||||
| 
 | ||||
| export interface ChromeAICallOptions extends BaseLanguageModelCallOptions {} | ||||
| export interface ChromeAICallOptions extends BaseLanguageModelCallOptions { } | ||||
| 
 | ||||
| function formatPrompt(messages: BaseMessage[]): string { | ||||
|   return messages | ||||
|     .map((message) => { | ||||
|       if (typeof message.content !== "string") { | ||||
|         // console.log(message.content)
 | ||||
|         // throw new Error(
 | ||||
|         //   "ChatChromeAI does not support non-string message content."
 | ||||
|         // )
 | ||||
|         if (message.content.length > 0) { | ||||
|           //@ts-ignore
 | ||||
|           return message.content[0]?.text || "" | ||||
| @ -66,31 +51,12 @@ function formatPrompt(messages: BaseMessage[]): string { | ||||
|     .join("\n") | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * To use this model you need to have the `Built-in AI Early Preview Program` | ||||
|  * for Chrome. You can find more information about the program here: | ||||
|  * @link https://developer.chrome.com/docs/ai/built-in
 | ||||
|  * | ||||
|  * @example | ||||
|  * ```typescript
 | ||||
|  * // Initialize the ChatChromeAI model.
 | ||||
|  * const model = new ChatChromeAI({ | ||||
|  *   temperature: 0.5, // Optional. Default is 0.5.
 | ||||
|  *   topK: 40, // Optional. Default is 40.
 | ||||
|  * }); | ||||
|  * | ||||
|  * // Call the model with a message and await the response.
 | ||||
|  * const response = await model.invoke([ | ||||
|  *   new HumanMessage({ content: "My name is John." }), | ||||
|  * ]); | ||||
|  * ``` | ||||
|  */ | ||||
| export class ChatChromeAI extends SimpleChatModel<ChromeAICallOptions> { | ||||
|   session?: AITextSession | ||||
| 
 | ||||
|   temperature = 0.5 | ||||
|   temperature = 0.8 | ||||
| 
 | ||||
|   topK = 40 | ||||
|   topK = 120 | ||||
| 
 | ||||
|   promptFormatter: (messages: BaseMessage[]) => string | ||||
| 
 | ||||
| @ -120,15 +86,14 @@ export class ChatChromeAI extends SimpleChatModel<ChromeAICallOptions> { | ||||
|       throw new Error("ChatChromeAI can only be used in the browser.") | ||||
|     } | ||||
| 
 | ||||
|     const { ai } = window as any | ||||
|     const canCreateTextSession = await ai.canCreateTextSession() | ||||
|     const canCreateTextSession = await checkChromeAIAvailability() | ||||
|     if (canCreateTextSession === AIModelAvailability.No) { | ||||
|       throw new Error("The AI model is not available.") | ||||
|     } else if (canCreateTextSession === AIModelAvailability.AfterDownload) { | ||||
|       throw new Error("The AI model is not yet downloaded.") | ||||
|     } | ||||
| 
 | ||||
|     this.session = await ai.createTextSession({ | ||||
|     this.session = await createAITextSession({ | ||||
|       topK: this.topK, | ||||
|       temperature: this.temperature | ||||
|     }) | ||||
|  | ||||
							
								
								
									
										54
									
								
								src/models/utils/chrome.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/models/utils/chrome.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| 
 | ||||
| export const checkChromeAIAvailability = async (): Promise<"readily" | "no" | "after-download"> => { | ||||
|     try { | ||||
|         const ai = (window as any).ai; | ||||
| 
 | ||||
|         // upcoming version change 
 | ||||
|         if (ai?.assistant?.capabilities) { | ||||
|             const capabilities = await ai.assistant.capabilities(); | ||||
|             return capabilities?.available ?? "no"; | ||||
|         } | ||||
| 
 | ||||
|         // old version 
 | ||||
|         if (ai?.canCreateTextSession) { | ||||
|             const available = await ai.canCreateTextSession(); | ||||
|             return available ?? "no"; | ||||
|         } | ||||
| 
 | ||||
|         return "no"; | ||||
|     } catch (e) { | ||||
|         console.error("Error checking Chrome AI availability:", e); | ||||
|         return "no"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export interface AITextSession { | ||||
|     prompt(input: string): Promise<string> | ||||
|     promptStreaming(input: string): ReadableStream | ||||
|     destroy(): void | ||||
|     clone(): AITextSession | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export const createAITextSession = async (data: any): Promise<AITextSession> => { | ||||
|     const ai = (window as any).ai; | ||||
| 
 | ||||
|     // upcoming version change 
 | ||||
|     if (ai?.assistant?.create) { | ||||
|         const session = await ai.assistant.create({ | ||||
|             ...data | ||||
|         }) | ||||
|         return session | ||||
|     } | ||||
| 
 | ||||
|     // old version
 | ||||
|     if (ai.createTextSession) { | ||||
|         const session = await ai.createTextSession({ | ||||
|             ...data | ||||
|         }) | ||||
| 
 | ||||
|         return session | ||||
|     } | ||||
| 
 | ||||
|     throw new Error("Chrome AI is not available.") | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/public/_locales/de/messages.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/public/_locales/de/messages.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| { | ||||
|     "extName": { | ||||
|         "message": "Page Assist - Eine Web-Benutzeroberfläche für lokale KI-Modelle" | ||||
|     }, | ||||
|     "extDescription": { | ||||
|         "message": "Nutzen Sie Ihre lokal laufenden KI-Modelle, um Sie beim Surfen im Web zu unterstützen." | ||||
|     }, | ||||
|     "openSidePanelToChat": { | ||||
|         "message": "Copilot zum Chatten öffnen" | ||||
|     }, | ||||
|     "openOptionToChat": { | ||||
|         "message": "Web-Benutzeroberfläche zum Chatten öffnen" | ||||
|     }, | ||||
|     "contextSummarize": { | ||||
|         "message": "Zusammenfassen" | ||||
|     }, | ||||
|     "contextExplain": { | ||||
|         "message": "Erklären" | ||||
|     }, | ||||
|     "contextRephrase": { | ||||
|         "message": "Umformulieren" | ||||
|     }, | ||||
|     "contextTranslate" :{ | ||||
|         "message": "Übersetzen" | ||||
|     }, | ||||
|     "contextCustom": { | ||||
|         "message": "Benutzerdefiniert" | ||||
|     } | ||||
| } | ||||
| @ -3,6 +3,8 @@ import { cleanUrl } from "../libs/clean-url" | ||||
| import { urlRewriteRuntime } from "../libs/runtime" | ||||
| import { getChromeAIModel } from "./chrome" | ||||
| import { setNoOfRetrievedDocs, setTotalFilePerKB } from "./app" | ||||
| import fetcher from "@/libs/fetcher" | ||||
| 
 | ||||
| 
 | ||||
| const storage = new Storage() | ||||
| 
 | ||||
| @ -82,7 +84,7 @@ export const defaultModel = async () => { | ||||
| export const isOllamaRunning = async () => { | ||||
|   try { | ||||
|     const baseUrl = await getOllamaURL() | ||||
|     const response = await fetch(`${cleanUrl(baseUrl)}`) | ||||
|     const response = await fetcher(`${cleanUrl(baseUrl)}`) | ||||
|     if (!response.ok) { | ||||
|       throw new Error(response.statusText) | ||||
|     } | ||||
| @ -100,7 +102,7 @@ export const getAllModels = async ({ | ||||
| }) => { | ||||
|   try { | ||||
|     const baseUrl = await getOllamaURL() | ||||
|     const response = await fetch(`${cleanUrl(baseUrl)}/api/tags`) | ||||
|     const response = await fetcher(`${cleanUrl(baseUrl)}/api/tags`) | ||||
|     if (!response.ok) { | ||||
|       if (returnEmpty) { | ||||
|         return [] | ||||
| @ -132,7 +134,7 @@ export const getAllModels = async ({ | ||||
| 
 | ||||
| export const deleteModel = async (model: string) => { | ||||
|   const baseUrl = await getOllamaURL() | ||||
|   const response = await fetch(`${cleanUrl(baseUrl)}/api/delete`, { | ||||
|   const response = await fetcher(`${cleanUrl(baseUrl)}/api/delete`, { | ||||
|     method: "DELETE", | ||||
|     headers: { | ||||
|       "Content-Type": "application/json" | ||||
| @ -154,7 +156,7 @@ export const fetchChatModels = async ({ | ||||
| }) => { | ||||
|   try { | ||||
|     const baseUrl = await getOllamaURL() | ||||
|     const response = await fetch(`${cleanUrl(baseUrl)}/api/tags`) | ||||
|     const response = await fetcher(`${cleanUrl(baseUrl)}/api/tags`) | ||||
|     if (!response.ok) { | ||||
|       if (returnEmpty) { | ||||
|         return [] | ||||
|  | ||||
| @ -49,8 +49,6 @@ type State = { | ||||
|   setChatMode: (chatMode: "normal" | "rag") => void | ||||
|   isEmbedding: boolean | ||||
|   setIsEmbedding: (isEmbedding: boolean) => void | ||||
|   speechToTextLanguage: string | ||||
|   setSpeechToTextLanguage: (language: string) => void | ||||
|   webSearch: boolean | ||||
|   setWebSearch: (webSearch: boolean) => void | ||||
|   isSearchingInternet: boolean | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| import { checkChromeAIAvailability } from "@/models/utils/chrome" | ||||
| 
 | ||||
| export const getChromeAISupported = async () => { | ||||
|   try { | ||||
|     let browserInfo = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./) | ||||
| @ -11,9 +13,8 @@ export const getChromeAISupported = async () => { | ||||
|       return "ai_not_supported" | ||||
|     } | ||||
| 
 | ||||
|     //@ts-ignore
 | ||||
|     const createSession = await ai?.canCreateGenericSession() | ||||
|     if (createSession !== "readily") { | ||||
|     const capabilities = await checkChromeAIAvailability() | ||||
|     if (capabilities !== "readily") { | ||||
|       return "ai_not_ready" | ||||
|     } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										19
									
								
								src/utils/key-down.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/utils/key-down.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| export const handleChatInputKeyDown = ({ | ||||
|     e, | ||||
|     sendWhenEnter, | ||||
|     typing, | ||||
|     isSending | ||||
| }: { | ||||
|     e: React.KeyboardEvent | ||||
|     typing: boolean | ||||
|     sendWhenEnter: boolean | ||||
|     isSending: boolean | ||||
| }) => { | ||||
|     return import.meta.env.BROWSER === "firefox" | ||||
|         ? e.key === "Enter" && | ||||
|         !e.shiftKey && | ||||
|         !e.nativeEvent.isComposing && | ||||
|         !isSending && | ||||
|         sendWhenEnter | ||||
|         : !typing && e.key === "Enter" && !e.shiftKey && !isSending && sendWhenEnter | ||||
| } | ||||
| @ -1,5 +1,5 @@ | ||||
| import { setBadgeBackgroundColor, setBadgeText, setTitle } from "@/utils/action" | ||||
| 
 | ||||
| import fetcher from "@/libs/fetcher" | ||||
| 
 | ||||
| export const progressHuman = (completed: number, total: number) => { | ||||
|     return ((completed / total) * 100).toFixed(0) + "%" | ||||
| @ -11,7 +11,7 @@ export const clearBadge = () => { | ||||
| } | ||||
| export const streamDownload = async (url: string, model: string) => { | ||||
|     url += "/api/pull" | ||||
|     const response = await fetch(url, { | ||||
|     const response = await fetcher(url, { | ||||
|         method: "POST", | ||||
|         headers: { | ||||
|             "Content-Type": "application/json" | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| import { cleanUrl } from "~/libs/clean-url" | ||||
| import fetcher from "@/libs/fetcher" | ||||
| 
 | ||||
| export const verifyPageShareURL = async (url: string) => { | ||||
|     const res = await fetch(`${cleanUrl(url)}/api/v1/ping`) | ||||
|     const res = await fetcher(`${cleanUrl(url)}/api/v1/ping`) | ||||
|     if (!res.ok) { | ||||
|         throw new Error("Unable to verify page share") | ||||
|     } | ||||
|  | ||||
| @ -50,7 +50,7 @@ export default defineConfig({ | ||||
|   outDir: "build", | ||||
| 
 | ||||
|   manifest: { | ||||
|     version: "1.2.1", | ||||
|     version: "1.2.2", | ||||
|     name: | ||||
|       process.env.TARGET === "firefox" | ||||
|         ? "Page Assist - A Web UI for Local AI Models" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user