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",
|
"rephrase": "Rephrase",
|
||||||
"translate": "Translate",
|
"translate": "Translate",
|
||||||
"custom": "Custom"
|
"custom": "Custom"
|
||||||
}
|
},
|
||||||
|
"citations": "Citations"
|
||||||
}
|
}
|
@ -94,5 +94,6 @@
|
|||||||
"explain": "Explicar",
|
"explain": "Explicar",
|
||||||
"rephrase": "Reformular",
|
"rephrase": "Reformular",
|
||||||
"translate": "Traducir"
|
"translate": "Traducir"
|
||||||
}
|
},
|
||||||
|
"citations": "Citas"
|
||||||
}
|
}
|
@ -88,5 +88,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advanced": "تنظیمات بیشتر مدل"
|
"advanced": "تنظیمات بیشتر مدل"
|
||||||
}
|
},
|
||||||
|
"citations": "منابع"
|
||||||
}
|
}
|
@ -94,5 +94,6 @@
|
|||||||
"explain": "Expliquer",
|
"explain": "Expliquer",
|
||||||
"rephrase": "Reformuler",
|
"rephrase": "Reformuler",
|
||||||
"translate": "Traduire"
|
"translate": "Traduire"
|
||||||
}
|
},
|
||||||
|
"citations": "Citations"
|
||||||
}
|
}
|
@ -93,5 +93,6 @@
|
|||||||
"explain": "Spiegare",
|
"explain": "Spiegare",
|
||||||
"rephrase": "Riformulare",
|
"rephrase": "Riformulare",
|
||||||
"translate": "Tradurre"
|
"translate": "Tradurre"
|
||||||
}
|
},
|
||||||
|
"citations": "Citazioni"
|
||||||
}
|
}
|
@ -94,5 +94,6 @@
|
|||||||
"explain": "説明",
|
"explain": "説明",
|
||||||
"rephrase": "言い換え",
|
"rephrase": "言い換え",
|
||||||
"translate": "翻訳"
|
"translate": "翻訳"
|
||||||
}
|
},
|
||||||
|
"citations": "引用"
|
||||||
}
|
}
|
@ -93,5 +93,6 @@
|
|||||||
"explain": "വിശദീകരിക്കുക",
|
"explain": "വിശദീകരിക്കുക",
|
||||||
"rephrase": "പുനഃരൂപീകരിക്കുക",
|
"rephrase": "പുനഃരൂപീകരിക്കുക",
|
||||||
"translate": "വിവർത്തനം ചെയ്യുക"
|
"translate": "വിവർത്തനം ചെയ്യുക"
|
||||||
}
|
},
|
||||||
|
"citations": "ഉദ്ധരണികൾ"
|
||||||
}
|
}
|
@ -94,5 +94,6 @@
|
|||||||
"explain": "Explicar",
|
"explain": "Explicar",
|
||||||
"rephrase": "Reformular",
|
"rephrase": "Reformular",
|
||||||
"translate": "Traduzir"
|
"translate": "Traduzir"
|
||||||
}
|
},
|
||||||
|
"citations": "Citações"
|
||||||
}
|
}
|
@ -93,5 +93,6 @@
|
|||||||
"explain": "Объяснить",
|
"explain": "Объяснить",
|
||||||
"rephrase": "Перефразировать",
|
"rephrase": "Перефразировать",
|
||||||
"translate": "Перевести"
|
"translate": "Перевести"
|
||||||
}
|
},
|
||||||
|
"citations": "Цитаты"
|
||||||
}
|
}
|
@ -94,5 +94,6 @@
|
|||||||
"explain": "解释",
|
"explain": "解释",
|
||||||
"rephrase": "重述",
|
"rephrase": "重述",
|
||||||
"translate": "翻译"
|
"translate": "翻译"
|
||||||
}
|
},
|
||||||
|
"citations": "引用"
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import Markdown from "../../Common/Markdown"
|
import Markdown from "../../Common/Markdown"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { Tag, Image, Tooltip } from "antd"
|
import { Tag, Image, Tooltip, Collapse } from "antd"
|
||||||
import { WebSearch } from "./WebSearch"
|
import { WebSearch } from "./WebSearch"
|
||||||
import {
|
import {
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
@ -127,15 +127,31 @@ export const PlaygroundMessage = (props: Props) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{props.isBot && props?.sources && props?.sources.length > 0 && (
|
{props.isBot && props?.sources && props?.sources.length > 0 && (
|
||||||
<div className="mb-3 flex flex-wrap gap-2">
|
<Collapse
|
||||||
{props?.sources?.map((source, index) => (
|
className="mt-6"
|
||||||
<MessageSource
|
ghost
|
||||||
onSourceClick={props.onSourceClick}
|
items={[
|
||||||
key={index}
|
{
|
||||||
source={source}
|
key: "1",
|
||||||
/>
|
label: (
|
||||||
))}
|
<div className="italic text-gray-500 dark:text-gray-400">
|
||||||
</div>
|
{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 && (
|
{!props.isProcessing && !editMode && (
|
||||||
<div
|
<div
|
||||||
|
@ -9,6 +9,7 @@ import { getPageShareUrl } from "~/services/ollama"
|
|||||||
import { cleanUrl } from "~/libs/clean-url"
|
import { cleanUrl } from "~/libs/clean-url"
|
||||||
import { getUserId, saveWebshare } from "@/db"
|
import { getUserId, saveWebshare } from "@/db"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
import fetcher from "@/libs/fetcher"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
messages: Message[]
|
messages: Message[]
|
||||||
@ -94,7 +95,7 @@ export const ShareBtn: React.FC<Props> = ({ messages }) => {
|
|||||||
const chat = reformatMessages(messages, values.name)
|
const chat = reformatMessages(messages, values.name)
|
||||||
const title = values.title
|
const title = values.title
|
||||||
const url = await getPageShareUrl()
|
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",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
|
@ -14,6 +14,7 @@ import { useTranslation } from "react-i18next"
|
|||||||
import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect"
|
import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect"
|
||||||
import { useSpeechRecognition } from "@/hooks/useSpeechRecognition"
|
import { useSpeechRecognition } from "@/hooks/useSpeechRecognition"
|
||||||
import { PiGlobe } from "react-icons/pi"
|
import { PiGlobe } from "react-icons/pi"
|
||||||
|
import { handleChatInputKeyDown } from "@/utils/key-down"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
dropedFile: File | undefined
|
dropedFile: File | undefined
|
||||||
@ -144,13 +145,16 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
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 (
|
if (
|
||||||
!typing &&
|
handleChatInputKeyDown({
|
||||||
e.key === "Enter" &&
|
e,
|
||||||
!e.shiftKey &&
|
sendWhenEnter,
|
||||||
!isSending &&
|
typing,
|
||||||
sendWhenEnter
|
isSending
|
||||||
|
})
|
||||||
) {
|
) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
stopListening()
|
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">
|
<div className="w-full border-x border-t flex flex-col dark:border-gray-600 rounded-t-xl p-2">
|
||||||
<textarea
|
<textarea
|
||||||
onCompositionStart={() => setTyping(true)}
|
onCompositionStart={() => {
|
||||||
onCompositionEnd={() => setTyping(false)}
|
if (import.meta.env.BROWSER !== "firefox") {
|
||||||
|
setTyping(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onCompositionEnd={() => {
|
||||||
|
if (import.meta.env.BROWSER !== "firefox") {
|
||||||
|
setTyping(false)
|
||||||
|
}
|
||||||
|
}}
|
||||||
onKeyDown={(e) => handleKeyDown(e)}
|
onKeyDown={(e) => handleKeyDown(e)}
|
||||||
ref={textareaRef}
|
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"
|
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 { Skeleton } from "antd"
|
||||||
import { cleanUrl } from "@/libs/clean-url"
|
import { cleanUrl } from "@/libs/clean-url"
|
||||||
import { Descriptions } from "antd"
|
import { Descriptions } from "antd"
|
||||||
|
import fetcher from "@/libs/fetcher"
|
||||||
|
|
||||||
export const AboutApp = () => {
|
export const AboutApp = () => {
|
||||||
const { t } = useTranslation("settings")
|
const { t } = useTranslation("settings")
|
||||||
@ -14,7 +15,7 @@ export const AboutApp = () => {
|
|||||||
const chromeVersion = browser.runtime.getManifest().version
|
const chromeVersion = browser.runtime.getManifest().version
|
||||||
try {
|
try {
|
||||||
const url = await getOllamaURL()
|
const url = await getOllamaURL()
|
||||||
const req = await fetch(`${cleanUrl(url)}/api/version`)
|
const req = await fetcher(`${cleanUrl(url)}/api/version`)
|
||||||
|
|
||||||
if (!req.ok) {
|
if (!req.ok) {
|
||||||
return {
|
return {
|
||||||
@ -75,11 +76,11 @@ export const AboutApp = () => {
|
|||||||
label: "X (formerly Twitter)",
|
label: "X (formerly Twitter)",
|
||||||
children: (
|
children: (
|
||||||
<a
|
<a
|
||||||
href="https://twitter.com/n4ze3m"
|
href="https://twitter.com/page_assist"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="text-blue-500 dark:text-blue-400">
|
className="text-blue-500 dark:text-blue-400">
|
||||||
@n4ze3m
|
@page_assist
|
||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,13 @@ import {
|
|||||||
import { useStorage } from "@plasmohq/storage/hook"
|
import { useStorage } from "@plasmohq/storage/hook"
|
||||||
|
|
||||||
export const GeneralSettings = () => {
|
export const GeneralSettings = () => {
|
||||||
const { clearChat, speechToTextLanguage, setSpeechToTextLanguage } =
|
const { clearChat } =
|
||||||
useMessageOption()
|
useMessageOption()
|
||||||
|
|
||||||
|
const [ speechToTextLanguage, setSpeechToTextLanguage ] = useStorage(
|
||||||
|
"speechToTextLanguage",
|
||||||
|
"en-US"
|
||||||
|
)
|
||||||
const [copilotResumeLastChat, setCopilotResumeLastChat] = useStorage(
|
const [copilotResumeLastChat, setCopilotResumeLastChat] = useStorage(
|
||||||
"copilotResumeLastChat",
|
"copilotResumeLastChat",
|
||||||
false
|
false
|
||||||
|
@ -7,6 +7,7 @@ import { deleteWebshare, getAllWebshares, getUserId } from "@/db"
|
|||||||
import { getPageShareUrl, setPageShareUrl } from "~/services/ollama"
|
import { getPageShareUrl, setPageShareUrl } from "~/services/ollama"
|
||||||
import { verifyPageShareURL } from "~/utils/verify-page-share"
|
import { verifyPageShareURL } from "~/utils/verify-page-share"
|
||||||
import { useStorage } from "@plasmohq/storage/hook"
|
import { useStorage } from "@plasmohq/storage/hook"
|
||||||
|
import fetcher from "@/libs/fetcher"
|
||||||
|
|
||||||
export const OptionShareBody = () => {
|
export const OptionShareBody = () => {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@ -48,7 +49,7 @@ export const OptionShareBody = () => {
|
|||||||
api_url: string
|
api_url: string
|
||||||
}) => {
|
}) => {
|
||||||
const owner_id = await getUserId()
|
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",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
|
@ -12,6 +12,7 @@ import { useTranslation } from "react-i18next"
|
|||||||
import { ModelSelect } from "@/components/Common/ModelSelect"
|
import { ModelSelect } from "@/components/Common/ModelSelect"
|
||||||
import { useSpeechRecognition } from "@/hooks/useSpeechRecognition"
|
import { useSpeechRecognition } from "@/hooks/useSpeechRecognition"
|
||||||
import { PiGlobeX, PiGlobe } from "react-icons/pi"
|
import { PiGlobeX, PiGlobe } from "react-icons/pi"
|
||||||
|
import { handleChatInputKeyDown } from "@/utils/key-down"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
dropedFile: File | undefined
|
dropedFile: File | undefined
|
||||||
@ -66,11 +67,12 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
|
|||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
if (e.key === "Process" || e.key === "229") return
|
if (e.key === "Process" || e.key === "229") return
|
||||||
if (
|
if (
|
||||||
e.key === "Enter" &&
|
handleChatInputKeyDown({
|
||||||
!e.shiftKey &&
|
e,
|
||||||
!isSending &&
|
sendWhenEnter,
|
||||||
sendWhenEnter &&
|
typing,
|
||||||
!typing
|
isSending
|
||||||
|
})
|
||||||
) {
|
) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
form.onSubmit(async (value) => {
|
form.onSubmit(async (value) => {
|
||||||
@ -245,8 +247,16 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
|
|||||||
rows={1}
|
rows={1}
|
||||||
style={{ minHeight: "60px" }}
|
style={{ minHeight: "60px" }}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onCompositionStart={() => setTyping(true)}
|
onCompositionStart={() => {
|
||||||
onCompositionEnd={() => setTyping(false)}
|
if (import.meta.env.BROWSER !== "firefox") {
|
||||||
|
setTyping(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onCompositionEnd={() => {
|
||||||
|
if (import.meta.env.BROWSER !== "firefox") {
|
||||||
|
setTyping(false)
|
||||||
|
}
|
||||||
|
}}
|
||||||
placeholder={t("form.textarea.placeholder")}
|
placeholder={t("form.textarea.placeholder")}
|
||||||
{...form.getInputProps("message")}
|
{...form.getInputProps("message")}
|
||||||
/>
|
/>
|
||||||
|
@ -53,7 +53,10 @@ export const SettingsBody = () => {
|
|||||||
const [hideCurrentChatModelSettings, setHideCurrentChatModelSettings] =
|
const [hideCurrentChatModelSettings, setHideCurrentChatModelSettings] =
|
||||||
useStorage("hideCurrentChatModelSettings", false)
|
useStorage("hideCurrentChatModelSettings", false)
|
||||||
|
|
||||||
const { speechToTextLanguage, setSpeechToTextLanguage } = useMessage()
|
const [ speechToTextLanguage, setSpeechToTextLanguage ] = useStorage(
|
||||||
|
"speechToTextLanguage",
|
||||||
|
"en-US"
|
||||||
|
)
|
||||||
const { mode, toggleDarkMode } = useDarkMode()
|
const { mode, toggleDarkMode } = useDarkMode()
|
||||||
|
|
||||||
const { changeLocale, locale, supportLanguage } = useI18n()
|
const { changeLocale, locale, supportLanguage } = useI18n()
|
||||||
|
@ -74,8 +74,6 @@ export const useMessage = () => {
|
|||||||
setChatMode,
|
setChatMode,
|
||||||
setIsEmbedding,
|
setIsEmbedding,
|
||||||
isEmbedding,
|
isEmbedding,
|
||||||
speechToTextLanguage,
|
|
||||||
setSpeechToTextLanguage,
|
|
||||||
currentURL,
|
currentURL,
|
||||||
setCurrentURL
|
setCurrentURL
|
||||||
} = useStoreMessage()
|
} = useStoreMessage()
|
||||||
@ -1230,8 +1228,6 @@ export const useMessage = () => {
|
|||||||
chatMode,
|
chatMode,
|
||||||
setChatMode,
|
setChatMode,
|
||||||
isEmbedding,
|
isEmbedding,
|
||||||
speechToTextLanguage,
|
|
||||||
setSpeechToTextLanguage,
|
|
||||||
regenerateLastMessage,
|
regenerateLastMessage,
|
||||||
webSearch,
|
webSearch,
|
||||||
setWebSearch,
|
setWebSearch,
|
||||||
|
@ -10,6 +10,7 @@ import { ja } from "./lang/ja";
|
|||||||
import { it } from "./lang/it";
|
import { it } from "./lang/it";
|
||||||
import { es } from "./lang/es";
|
import { es } from "./lang/es";
|
||||||
import { fa } from "./lang/fa";
|
import { fa } from "./lang/fa";
|
||||||
|
import { de } from "./lang/de";
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
@ -30,7 +31,8 @@ i18n
|
|||||||
ja: ja,
|
ja: ja,
|
||||||
"ja-JP": ja,
|
"ja-JP": ja,
|
||||||
fa: fa,
|
fa: fa,
|
||||||
"fa-IR": fa
|
"fa-IR": fa,
|
||||||
|
de: de
|
||||||
},
|
},
|
||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
lng: localStorage.getItem("i18nextLng") || "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: "فارسی",
|
label: "فارسی",
|
||||||
value: "fa"
|
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 { BaseMessage, AIMessageChunk } from "@langchain/core/messages"
|
||||||
import { ChatGenerationChunk } from "@langchain/core/outputs"
|
import { ChatGenerationChunk } from "@langchain/core/outputs"
|
||||||
import { IterableReadableStream } from "@langchain/core/utils/stream"
|
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 {
|
export interface AITextSessionOptions {
|
||||||
topK: number
|
topK: number
|
||||||
@ -44,16 +33,12 @@ export interface ChromeAIInputs extends BaseChatModelParams {
|
|||||||
promptFormatter?: (messages: BaseMessage[]) => string
|
promptFormatter?: (messages: BaseMessage[]) => string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChromeAICallOptions extends BaseLanguageModelCallOptions {}
|
export interface ChromeAICallOptions extends BaseLanguageModelCallOptions { }
|
||||||
|
|
||||||
function formatPrompt(messages: BaseMessage[]): string {
|
function formatPrompt(messages: BaseMessage[]): string {
|
||||||
return messages
|
return messages
|
||||||
.map((message) => {
|
.map((message) => {
|
||||||
if (typeof message.content !== "string") {
|
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) {
|
if (message.content.length > 0) {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
return message.content[0]?.text || ""
|
return message.content[0]?.text || ""
|
||||||
@ -66,31 +51,12 @@ function formatPrompt(messages: BaseMessage[]): string {
|
|||||||
.join("\n")
|
.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> {
|
export class ChatChromeAI extends SimpleChatModel<ChromeAICallOptions> {
|
||||||
session?: AITextSession
|
session?: AITextSession
|
||||||
|
|
||||||
temperature = 0.5
|
temperature = 0.8
|
||||||
|
|
||||||
topK = 40
|
topK = 120
|
||||||
|
|
||||||
promptFormatter: (messages: BaseMessage[]) => string
|
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.")
|
throw new Error("ChatChromeAI can only be used in the browser.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const { ai } = window as any
|
const canCreateTextSession = await checkChromeAIAvailability()
|
||||||
const canCreateTextSession = await ai.canCreateTextSession()
|
|
||||||
if (canCreateTextSession === AIModelAvailability.No) {
|
if (canCreateTextSession === AIModelAvailability.No) {
|
||||||
throw new Error("The AI model is not available.")
|
throw new Error("The AI model is not available.")
|
||||||
} else if (canCreateTextSession === AIModelAvailability.AfterDownload) {
|
} else if (canCreateTextSession === AIModelAvailability.AfterDownload) {
|
||||||
throw new Error("The AI model is not yet downloaded.")
|
throw new Error("The AI model is not yet downloaded.")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.session = await ai.createTextSession({
|
this.session = await createAITextSession({
|
||||||
topK: this.topK,
|
topK: this.topK,
|
||||||
temperature: this.temperature
|
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 { urlRewriteRuntime } from "../libs/runtime"
|
||||||
import { getChromeAIModel } from "./chrome"
|
import { getChromeAIModel } from "./chrome"
|
||||||
import { setNoOfRetrievedDocs, setTotalFilePerKB } from "./app"
|
import { setNoOfRetrievedDocs, setTotalFilePerKB } from "./app"
|
||||||
|
import fetcher from "@/libs/fetcher"
|
||||||
|
|
||||||
|
|
||||||
const storage = new Storage()
|
const storage = new Storage()
|
||||||
|
|
||||||
@ -82,7 +84,7 @@ export const defaultModel = async () => {
|
|||||||
export const isOllamaRunning = async () => {
|
export const isOllamaRunning = async () => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = await getOllamaURL()
|
const baseUrl = await getOllamaURL()
|
||||||
const response = await fetch(`${cleanUrl(baseUrl)}`)
|
const response = await fetcher(`${cleanUrl(baseUrl)}`)
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(response.statusText)
|
throw new Error(response.statusText)
|
||||||
}
|
}
|
||||||
@ -100,7 +102,7 @@ export const getAllModels = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = await getOllamaURL()
|
const baseUrl = await getOllamaURL()
|
||||||
const response = await fetch(`${cleanUrl(baseUrl)}/api/tags`)
|
const response = await fetcher(`${cleanUrl(baseUrl)}/api/tags`)
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (returnEmpty) {
|
if (returnEmpty) {
|
||||||
return []
|
return []
|
||||||
@ -132,7 +134,7 @@ export const getAllModels = async ({
|
|||||||
|
|
||||||
export const deleteModel = async (model: string) => {
|
export const deleteModel = async (model: string) => {
|
||||||
const baseUrl = await getOllamaURL()
|
const baseUrl = await getOllamaURL()
|
||||||
const response = await fetch(`${cleanUrl(baseUrl)}/api/delete`, {
|
const response = await fetcher(`${cleanUrl(baseUrl)}/api/delete`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@ -154,7 +156,7 @@ export const fetchChatModels = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = await getOllamaURL()
|
const baseUrl = await getOllamaURL()
|
||||||
const response = await fetch(`${cleanUrl(baseUrl)}/api/tags`)
|
const response = await fetcher(`${cleanUrl(baseUrl)}/api/tags`)
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (returnEmpty) {
|
if (returnEmpty) {
|
||||||
return []
|
return []
|
||||||
|
@ -49,8 +49,6 @@ type State = {
|
|||||||
setChatMode: (chatMode: "normal" | "rag") => void
|
setChatMode: (chatMode: "normal" | "rag") => void
|
||||||
isEmbedding: boolean
|
isEmbedding: boolean
|
||||||
setIsEmbedding: (isEmbedding: boolean) => void
|
setIsEmbedding: (isEmbedding: boolean) => void
|
||||||
speechToTextLanguage: string
|
|
||||||
setSpeechToTextLanguage: (language: string) => void
|
|
||||||
webSearch: boolean
|
webSearch: boolean
|
||||||
setWebSearch: (webSearch: boolean) => void
|
setWebSearch: (webSearch: boolean) => void
|
||||||
isSearchingInternet: boolean
|
isSearchingInternet: boolean
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { checkChromeAIAvailability } from "@/models/utils/chrome"
|
||||||
|
|
||||||
export const getChromeAISupported = async () => {
|
export const getChromeAISupported = async () => {
|
||||||
try {
|
try {
|
||||||
let browserInfo = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)
|
let browserInfo = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)
|
||||||
@ -11,9 +13,8 @@ export const getChromeAISupported = async () => {
|
|||||||
return "ai_not_supported"
|
return "ai_not_supported"
|
||||||
}
|
}
|
||||||
|
|
||||||
//@ts-ignore
|
const capabilities = await checkChromeAIAvailability()
|
||||||
const createSession = await ai?.canCreateGenericSession()
|
if (capabilities !== "readily") {
|
||||||
if (createSession !== "readily") {
|
|
||||||
return "ai_not_ready"
|
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 { setBadgeBackgroundColor, setBadgeText, setTitle } from "@/utils/action"
|
||||||
|
import fetcher from "@/libs/fetcher"
|
||||||
|
|
||||||
export const progressHuman = (completed: number, total: number) => {
|
export const progressHuman = (completed: number, total: number) => {
|
||||||
return ((completed / total) * 100).toFixed(0) + "%"
|
return ((completed / total) * 100).toFixed(0) + "%"
|
||||||
@ -11,7 +11,7 @@ export const clearBadge = () => {
|
|||||||
}
|
}
|
||||||
export const streamDownload = async (url: string, model: string) => {
|
export const streamDownload = async (url: string, model: string) => {
|
||||||
url += "/api/pull"
|
url += "/api/pull"
|
||||||
const response = await fetch(url, {
|
const response = await fetcher(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { cleanUrl } from "~/libs/clean-url"
|
import { cleanUrl } from "~/libs/clean-url"
|
||||||
|
import fetcher from "@/libs/fetcher"
|
||||||
|
|
||||||
export const verifyPageShareURL = async (url: string) => {
|
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) {
|
if (!res.ok) {
|
||||||
throw new Error("Unable to verify page share")
|
throw new Error("Unable to verify page share")
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ export default defineConfig({
|
|||||||
outDir: "build",
|
outDir: "build",
|
||||||
|
|
||||||
manifest: {
|
manifest: {
|
||||||
version: "1.2.1",
|
version: "1.2.2",
|
||||||
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