Merge branch 'next' into add-pt-locale
This commit is contained in:
commit
b3d9f3ca92
@ -19,10 +19,10 @@ Thank you for your interest in contributing to Page Assist! We welcome contribut
|
||||
|
||||
3. **Install dependencies**
|
||||
|
||||
Page Assist uses [Yarn](https://yarnpkg.com/) for dependency management. Install the required dependencies by running the following command in the project root directory:
|
||||
Page Assist uses [Bun](https://bun.sh/) for dependency management. Install the required dependencies by running the following command in the project root directory:
|
||||
|
||||
```
|
||||
yarn install
|
||||
bun install
|
||||
```
|
||||
|
||||
4. **Start the development server**
|
||||
@ -30,10 +30,16 @@ Thank you for your interest in contributing to Page Assist! We welcome contribut
|
||||
To run the extension in development mode, use the following command:
|
||||
|
||||
```
|
||||
yarn dev
|
||||
bun dev
|
||||
```
|
||||
|
||||
This will open a browser window with the extension loaded.
|
||||
This will open a chrome browser window with the extension loaded.
|
||||
|
||||
for firefox:
|
||||
|
||||
```
|
||||
bun dev:firefox
|
||||
```
|
||||
|
||||
5. **Install Ollama locally**
|
||||
|
||||
|
11
README.md
11
README.md
@ -30,13 +30,6 @@ Checkout the Demo (v1.0.0):
|
||||
|
||||
want more features? Create an issue and let me know.
|
||||
|
||||
<!-- ## Installation
|
||||
|
||||
### Chrome Web Store
|
||||
|
||||
You can install the extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/page-assist-a-web-ui-for/jfgfiigpkhlkbnfnbobbkinehhfdhndo)
|
||||
|
||||
Note: You can install the extension on any Chromium-based browser. It is not limited to Chrome. -->
|
||||
|
||||
### Manual Installation
|
||||
|
||||
@ -125,6 +118,8 @@ This will start a development server and watch for changes in the source files.
|
||||
| Opera | ❌ | ❌ | ✅ |
|
||||
| Arc | ❌ | ❌ | ✅ |
|
||||
|
||||
|
||||
|
||||
## Local AI Provider
|
||||
|
||||
- [Ollama](https://github.com/ollama/ollama) (Currently the only supported provider. More providers will be added in the future.)
|
||||
@ -132,8 +127,8 @@ This will start a development server and watch for changes in the source files.
|
||||
## Roadmap
|
||||
|
||||
- [X] Firefox Support
|
||||
- [ ] Code Completion support for Web based IDEs (like Colab, Jupyter, etc.)
|
||||
- [ ] More Local AI Providers
|
||||
- [ ] More Features
|
||||
- [ ] More Customization Options
|
||||
- [ ] Better UI/UX
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
"dev": "cross-env TARGET=chrome wxt",
|
||||
"dev:firefox": "cross-env TARGET=firefox wxt -b firefox",
|
||||
"build": "cross-env TARGET=chrome wxt build",
|
||||
"build:firefox": "cross-env TARGET=chrome cross-env TARGET=firefox wxt build -b firefox",
|
||||
"build:firefox": "cross-env TARGET=firefox wxt build -b firefox",
|
||||
"zip": "cross-env TARGET=chrome wxt zip",
|
||||
"zip:firefox": "cross-env TARGET=firefox wxt zip -b firefox",
|
||||
"compile": "tsc --noEmit",
|
||||
|
88
src/assets/locale/fr/common.json
Normal file
88
src/assets/locale/fr/common.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"pageAssist": "Page Assist",
|
||||
"selectAModel": "Sélectionnez un modèle",
|
||||
"save": "Sauvegarder",
|
||||
"saved": "Enregistrée",
|
||||
"cancel": "Annuler",
|
||||
"retry": "Recommencez",
|
||||
"share": {
|
||||
"tooltip": {
|
||||
"share": "Partager"
|
||||
},
|
||||
"modal": {
|
||||
"title": "Partagez le lien vers le chat"
|
||||
},
|
||||
"form": {
|
||||
"defaultValue": {
|
||||
"name": "Anonyme",
|
||||
"title": "Chat sans titre"
|
||||
},
|
||||
"title": {
|
||||
"label": "Titre de chat",
|
||||
"placeholder": "Entrez le titre du chat",
|
||||
"required": "Le titre de chat est requis"
|
||||
},
|
||||
"name": {
|
||||
"label": "Votre nom",
|
||||
"placeholder": "Entrez votre nome",
|
||||
"required": "Votre nom est requis"
|
||||
},
|
||||
"btn": {
|
||||
"save": "Générer un lien",
|
||||
"saving": "Génération de lien..."
|
||||
}
|
||||
},
|
||||
"notification": {
|
||||
"successGenerate": "Lien copié dans le presse-papiers",
|
||||
"failGenerate": "Échec de la génération de lien"
|
||||
}
|
||||
},
|
||||
"copyToClipboard": "Copier dans le presse-papier",
|
||||
"webSearch": "Recherche sur le Web",
|
||||
"regenerate": "Régénérer",
|
||||
"edit": "Modifier",
|
||||
"saveAndSubmit": "Enregistrer et soumettre",
|
||||
"editMessage": {
|
||||
"placeholder": "Tapez un message..."
|
||||
},
|
||||
"submit": "Soumettre",
|
||||
"noData": "Pas de données",
|
||||
"noHistory": "Pas d'historique de chat",
|
||||
"chatWithCurrentPage": "Discuter avec la page actuelle",
|
||||
"beta": "Bêta",
|
||||
"tts": "Synthèse vocale",
|
||||
"currentChatModelSettings":"Paramètres actuels du modèle de chat",
|
||||
"modelSettings": {
|
||||
"label": "Paramètres du modèle",
|
||||
"description": "Définissez les options de modèle globale pour tous les chats",
|
||||
"form": {
|
||||
"keepAlive": {
|
||||
"label": "Maintenir en mémoire",
|
||||
"help": "contrôle combien de temps le modèle restera chargé en mémoire après la demande (par défaut: 5m)",
|
||||
"placeholder": "Entrer la durée du maintien en mémoire (p. ex., 5 m, 10 m, 1 h)"
|
||||
},
|
||||
"temperature": {
|
||||
"label": "Température",
|
||||
"placeholder": "Entrez la valeur de la température (par exemple 0,7, 1,0)"
|
||||
},
|
||||
"numCtx": {
|
||||
"label": "Nombre de contextes",
|
||||
"placeholder": "Entrez la valeur du nombre de contextes (par défaut: 2048)"
|
||||
},
|
||||
"seed": {
|
||||
"label": "Graine",
|
||||
"placeholder": "Entrez la valeur des semences (par exemple 1234)",
|
||||
"help": "Reproductibilité de la sortie du modèle"
|
||||
},
|
||||
"topK": {
|
||||
"label": "Top K",
|
||||
"placeholder": "Entrez la valeur Top K (par exemple 40, 100)"
|
||||
},
|
||||
"topP": {
|
||||
"label": "Top P",
|
||||
"placeholder": "Entrez la valeur Top P (par exemple 0,9, 0,95)"
|
||||
}
|
||||
},
|
||||
"advanced": "Plus de paramètres du modèle"
|
||||
}
|
||||
}
|
42
src/assets/locale/fr/knowledge.json
Normal file
42
src/assets/locale/fr/knowledge.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"addBtn": "Ajouter de nouvelles connaissances",
|
||||
"columns": {
|
||||
"title": "Titre",
|
||||
"status": "Statut",
|
||||
"embeddings": "Modèle d'embedding",
|
||||
"createdAt": "Créé à",
|
||||
"action": "actions"
|
||||
},
|
||||
"expandedColumns": {
|
||||
"name": "Nom"
|
||||
},
|
||||
"tooltip": {
|
||||
"delete": "Supprimer"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Êtes-vous sûr de vouloir supprimer ces connaissances ?"
|
||||
},
|
||||
"deleteSuccess": "Connaissances supprimées avec succès",
|
||||
"status": {
|
||||
"pending": "En attente",
|
||||
"finished": "Terminé",
|
||||
"processing": "Traitement"
|
||||
},
|
||||
"addKnowledge": "Ajouter des connaissances",
|
||||
"form": {
|
||||
"title": {
|
||||
"label": "Titre de la connaissance",
|
||||
"placeholder": "Entrez le titre de la connaissances",
|
||||
"required": "Le titre de la connaissance est requis"
|
||||
},
|
||||
"uploadFile": {
|
||||
"label": "Téléverser un fichier",
|
||||
"uploadText": "Faites glisser et déposez un fichier ici ou cliquez pour téléverser",
|
||||
"uploadHint": "Types de fichiers pris en charge: .pdf, .csv, .txt, .md, .docx",
|
||||
"required": "Le fichier est requis"
|
||||
},
|
||||
"submit": "Soumettre",
|
||||
"success": "Les connaissances ont ajouté avec succès"
|
||||
},
|
||||
"noEmbeddingModel": "Veuillez d'abord ajouter un modèle d'embedding depuis la page des paramètres de Ollama"
|
||||
}
|
12
src/assets/locale/fr/option.json
Normal file
12
src/assets/locale/fr/option.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"newChat": "Nouveau chat",
|
||||
"selectAPrompt": "Sélectionnez un prompt",
|
||||
"githubRepository": "Référentiel GitHub",
|
||||
"settings": "Paramètres",
|
||||
"sidebarTitle": "Historique de chat",
|
||||
"error": "Erreur",
|
||||
"somethingWentWrong": "Quelque chose s'est mal passé",
|
||||
"validationSelectModel": "Veuillez sélectionner un modèle pour continuer",
|
||||
"deleteHistoryConfirmation": "Êtes-vous sûr de vouloir supprimer cette historique ?",
|
||||
"editHistoryTitle": "Entrez un nouveau titre"
|
||||
}
|
29
src/assets/locale/fr/playground.json
Normal file
29
src/assets/locale/fr/playground.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"ollamaState": {
|
||||
"searching": "Searching for Your Ollama 🦙",
|
||||
"running": "Ollama is running 🦙",
|
||||
"notRunning": "Unable to connect to Ollama 🦙",
|
||||
"connectionError": "It seems like you are having a connection error. Please refer to this <anchor>documentation</anchor> for troubleshooting."
|
||||
},
|
||||
"formError": {
|
||||
"noModel": "Please select a model",
|
||||
"noEmbeddingModel": "Please set an embedding model on the Settings > Ollama page"
|
||||
},
|
||||
"form": {
|
||||
"textarea": {
|
||||
"placeholder": "Type a message..."
|
||||
},
|
||||
"webSearch": {
|
||||
"on": "On",
|
||||
"off": "Off"
|
||||
}
|
||||
},
|
||||
"tooltip": {
|
||||
"searchInternet": "Rechercher Internet",
|
||||
"speechToText": "Parole en texte",
|
||||
"uploadImage": "Téléverser une image",
|
||||
"stopStreaming": "Arrêtez la diffusion",
|
||||
"knowledge": "Connaissance"
|
||||
},
|
||||
"sendWhenEnter": "Envoyer en appuyant sur Entrée"
|
||||
}
|
286
src/assets/locale/fr/settings.json
Normal file
286
src/assets/locale/fr/settings.json
Normal file
@ -0,0 +1,286 @@
|
||||
{
|
||||
"generalSettings": {
|
||||
"title": "Réglages généraux",
|
||||
"settings": {
|
||||
"heading": "Paramètres d'interface utilisateur Web",
|
||||
"speechRecognitionLang": {
|
||||
"label": "Langue de reconnaissance vocale",
|
||||
"placeholder": "Sélectionnez une langue"
|
||||
},
|
||||
"language": {
|
||||
"label": "Langue",
|
||||
"placeholder": "Sélectionnez une langue"
|
||||
},
|
||||
"darkMode": {
|
||||
"label": "Change le thème",
|
||||
"options": {
|
||||
"light": "Clair",
|
||||
"dark": "Sombre"
|
||||
}
|
||||
},
|
||||
"copilotResumeLastChat": {
|
||||
"label": "Reprendre la dernière conversation lors de l'ouverture du sidepanel (Copilot)"
|
||||
},
|
||||
"hideCurrentChatModelSettings": {
|
||||
"label": "Masquer les paramètres actuels du modèle de chat"
|
||||
}
|
||||
},
|
||||
"webSearch": {
|
||||
"heading": "Gérer la recherche Web",
|
||||
"searchMode": {
|
||||
"label": "Effectuer une simple recherche sur Internet"
|
||||
},
|
||||
"provider": {
|
||||
"label": "Moteur de recherche",
|
||||
"placeholder": "Sélectionnez un moteur de recherche"
|
||||
},
|
||||
"totalSearchResults": {
|
||||
"label": "Résultats de la recherche totaux",
|
||||
"placeholder": "Entrez les résultats de la recherche totaux"
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"heading": "Les paramètres du système",
|
||||
"deleteChatHistory": {
|
||||
"label": "Supprimer l'historique du chat",
|
||||
"button": "Supprimer",
|
||||
"confirm": "Êtes-vous sûr de vouloir supprimer l'historique de votre chat? Cette action ne peut pas être annulée."
|
||||
},
|
||||
"export": {
|
||||
"label": "Exporter l'historique du chat, la base de connaissances et les invites",
|
||||
"button": "Exporter des données",
|
||||
"success": "Succès de l'exportation"
|
||||
},
|
||||
"import": {
|
||||
"label": "Importer l'historique du chat, la base de connaissances et les invites",
|
||||
"button": "Importer des données",
|
||||
"success": "Succès d'importation",
|
||||
"error": "Erreur d'importation"
|
||||
}
|
||||
},
|
||||
"tts": {
|
||||
"heading": "Paramètres de synthèse vocale",
|
||||
"ttsEnabled": {
|
||||
"label": "Activer la synthèse vocale"
|
||||
},
|
||||
"ttsProvider": {
|
||||
"label": "Fournisseur de synthèse vocale",
|
||||
"placeholder": "Sélectionnez un fournisseur"
|
||||
},
|
||||
"ttsVoice": {
|
||||
"label": "Voix de synthèse vocale",
|
||||
"placeholder": "Sélectionnez une voix"
|
||||
},
|
||||
"ssmlEnabled": {
|
||||
"label": "Activer SSML (langage de balisage de synthèse vocale)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"manageModels": {
|
||||
"title": "Gérer les modèles",
|
||||
"addBtn": "Ajouter un nouveau modèle",
|
||||
"columns": {
|
||||
"name": "Nom",
|
||||
"digest": "Digérer",
|
||||
"modifiedAt": "Modifié à",
|
||||
"size": "Taille",
|
||||
"actions": "Actions"
|
||||
},
|
||||
"expandedColumns": {
|
||||
"parentModel": "Modèle parent",
|
||||
"format": "Format",
|
||||
"family": "Famille",
|
||||
"parameterSize": "Taille du paramètre",
|
||||
"quantizationLevel": "Niveau de quantification"
|
||||
},
|
||||
"tooltip": {
|
||||
"delete": "Supprimer le modèle",
|
||||
"repull": "Modèle de ré-échoue"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Êtes-vous sûr de vouloir supprimer ce modèle?",
|
||||
"repull": "Êtes-vous sûr de vouloir rétracter ce modèle?"
|
||||
},
|
||||
"modal": {
|
||||
"title": "Ajouter un nouveau modèle",
|
||||
"placeholder": "Entrez le nom du modèle",
|
||||
"pull": "Modèle de traction"
|
||||
},
|
||||
"notification": {
|
||||
"pullModel": "Modèle de traction",
|
||||
"pullModelDescription": "Tirling {{ModelName}} modèle. Pour plus de détails, vérifiez l'icône d'extension.",
|
||||
"success": "Succès",
|
||||
"error": "Erreur",
|
||||
"successDescription": "A réussi à tirer le modèle",
|
||||
"successDeleteDescription": "Supprimé avec succès le modèle",
|
||||
"someError": "Quelque chose s'est mal passé.Veuillez réessayer plus tard"
|
||||
}
|
||||
},
|
||||
"managePrompts": {
|
||||
"title": "Gérer les prompts",
|
||||
"addBtn": "Ajouter un nouveau prompt",
|
||||
"option1": "Normale",
|
||||
"option2": "RAG",
|
||||
"questionPrompt": "Prompt de question",
|
||||
"columns": {
|
||||
"title": "Titre",
|
||||
"prompt": "Prompt",
|
||||
"type": "Type de prompt",
|
||||
"actions": "Actions"
|
||||
},
|
||||
"systemPrompt": "Prompt système",
|
||||
"quickPrompt": "Prompt rapide",
|
||||
"tooltip": {
|
||||
"delete": "Supprimer le prompt",
|
||||
"edit": "Modifier le prompt"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Êtes-vous sûr de vouloir supprimer cette invite ? Cette action ne peut pas être annulée."
|
||||
},
|
||||
"modal": {
|
||||
"addTitle": "Ajouter un nouveau prompt",
|
||||
"editTitle": "Modifier le prompt"
|
||||
},
|
||||
"form": {
|
||||
"title": {
|
||||
"label": "Titre",
|
||||
"placeholder": "Mon super prompt",
|
||||
"required": "Veuillez saisir un titre"
|
||||
},
|
||||
"prompt": {
|
||||
"label": "Prompt",
|
||||
"placeholder": "Entrer Prompt",
|
||||
"required": "Veuillez entrer un prompt",
|
||||
"help": "Vous pouvez utiliser {key} comme variable dans votre prompt."
|
||||
},
|
||||
"isSystem": {
|
||||
"label": "Est un prompt système"
|
||||
},
|
||||
"btnSave": {
|
||||
"saving": "Ajout de Prompt...",
|
||||
"save": "Ajouter un prompt"
|
||||
},
|
||||
"btnEdit": {
|
||||
"saving": "Mise à jour du Prompt...",
|
||||
"save": "Modifier le prompt"
|
||||
}
|
||||
},
|
||||
"notification": {
|
||||
"addSuccess": "Prompt ajouté",
|
||||
"addSuccessDesc": "Le prompt a été ajoutée avec succès",
|
||||
"error": "Erreur",
|
||||
"someError": "Quelque chose s'est mal passé. Veuillez réessayer plus tard",
|
||||
"updatedSuccess": "Prompt mise à jour",
|
||||
"updatedSuccessDesc": "Le prompt a été mis à jour avec succès",
|
||||
"deletedSuccess": "Prompt supprimé",
|
||||
"deletedSuccessDesc": "Le prompt a été supprimé avec succès"
|
||||
}
|
||||
},
|
||||
"manageShare": {
|
||||
"title": "Gérer le partage",
|
||||
"heading": "Configurer l'URL de partage de la page",
|
||||
"form": {
|
||||
"url": {
|
||||
"label": "URL de partage de page",
|
||||
"placeholder": "Entrez l'URL de partage de la page",
|
||||
"required": "Veuillez saisir URL de partage de votre page!",
|
||||
"help": "Pour des raisons de confidentialité, vous pouvez auto-héberger le partage de la page et fournir l'URL ici.<anchor>En savoir plus</anchor>."
|
||||
}
|
||||
},
|
||||
"webshare": {
|
||||
"heading": "Partage Web",
|
||||
"columns": {
|
||||
"title": "Titre",
|
||||
"url": "URL",
|
||||
"actions": "Actions"
|
||||
},
|
||||
"tooltip": {
|
||||
"delete": "Supprimer le partage"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Êtes-vous sûr de vouloir supprimer ce partage ? Cette action ne peut pas être annulée."
|
||||
},
|
||||
"label": "Gérer le partage de pages",
|
||||
"description": "Activer ou désactiver la fonction de partage de page"
|
||||
},
|
||||
"notification": {
|
||||
"pageShareSuccess": "URL de partage de page mise à jour avec succès",
|
||||
"someError": "Quelque chose a mal tourné. Veuillez réessayer plus tard",
|
||||
"webShareDeleteSuccess": "Partage Web supprimé avec succès"
|
||||
}
|
||||
},
|
||||
"ollamaSettings": {
|
||||
"title": "Réglages de Ollama",
|
||||
"heading": "Configurer Ollama",
|
||||
"settings": {
|
||||
"ollamaUrl": {
|
||||
"label": "Url de Ollama",
|
||||
"placeholder": "Entrer l'url de Ollama"
|
||||
},
|
||||
"ragSettings": {
|
||||
"label": "Paramètres de RAG",
|
||||
"model": {
|
||||
"label": "Modèle d'embedding",
|
||||
"required": "Veuillez sélectionner un modèle",
|
||||
"help": "Fortement recommandé d'utiliser des modèles d'embedding comme «momic-embed-text».",
|
||||
"placeholder": "Sélectionnez un modèle"
|
||||
},
|
||||
"chunkSize": {
|
||||
"label": "Taille",
|
||||
"placeholder": "Entrez la taille du morceau",
|
||||
"required": "Veuillez saisir une taille"
|
||||
},
|
||||
"chunkOverlap": {
|
||||
"label": "Chevauchement",
|
||||
"placeholder": "Entrez le chevauchement des morceaux",
|
||||
"required": "Veuillez saisir un chevauchement"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"label": "Configure RAG Prompt",
|
||||
"option1": "Normal",
|
||||
"option2": "Web",
|
||||
"alert": "La configuration du prompt système ici est déconseillée. Veuillez utiliser la section Gérer les prompts pour ajouter...",
|
||||
"systemPrompt": "Prompt système",
|
||||
"systemPromptPlaceholder": "Entrez le prompt système",
|
||||
"webSearchPrompt": "Prompt de recherche Web",
|
||||
"webSearchPromptHelp": "Ne supprimez pas `{search_results}` du prompt.",
|
||||
"webSearchPromptError": "Veuillez saisir un prompt de recherche Web",
|
||||
"webSearchPromptPlaceholder": "Entrez le prompt de recherche Web",
|
||||
"webSearchFollowUpPrompt": "Prompt de suivi de recherche Web",
|
||||
"webSearchFollowUpPromptHelp": "Ne supprimez pas `{chat_history}` et `{question}` du prompt.",
|
||||
"webSearchFollowUpPromptError": "Veuillez saisir votre prompt de suivi de recherche Web!",
|
||||
"webSearchFollowUpPromptPlaceholder": "Votre prompt de suivi de recherche Web"
|
||||
},
|
||||
"advanced": {
|
||||
"label": "Configuration avancée de l'URL de Ollama",
|
||||
"urlRewriteEnabled": {
|
||||
"label": "Activer ou désactiver l'URL d'origine personnalisée"
|
||||
},
|
||||
"rewriteUrl": {
|
||||
"label": "URL d'origine personnalisée",
|
||||
"placeholder": "Entrez l'URL d'origine personnalisée"
|
||||
},
|
||||
"help": "Si vous avez des problèmes de connexion avec OLLAMA sur Page Assist, vous pouvez configurer une URL d'origine personnalisée. Pour en savoir plus sur la configuration, <anchor>cliquez ici</anchor>."
|
||||
}
|
||||
}
|
||||
},
|
||||
"manageSearch": {
|
||||
"title": "Gérer la recherche Web",
|
||||
"heading": "Configurer la recherche Web"
|
||||
},
|
||||
"about": {
|
||||
"title": "À propos",
|
||||
"heading": "À propos",
|
||||
"chromeVersion": "Version de Page Assist",
|
||||
"ollamaVersion": "Version de Ollama",
|
||||
"support": "Vous pouvez supporter le projet Page Assist en donnant ou parrainant via les plateformes suivantes:",
|
||||
"koFi": "Supporter sur ko-fi",
|
||||
"githubSponsor": "Sponsoriser sur github",
|
||||
"githubRepo": "Référentiel GitHub"
|
||||
},
|
||||
"manageKnowledge": {
|
||||
"title": "Gérer les connaissances",
|
||||
"heading": "Configurer la base de connaissances"
|
||||
}
|
||||
}
|
7
src/assets/locale/fr/sidepanel.json
Normal file
7
src/assets/locale/fr/sidepanel.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"tooltip": {
|
||||
"embed": "Cela peut prendre quelques minutes pour intégrer la page. S'il vous plaît, patientez...",
|
||||
"clear": "Effacer l'historique du chat",
|
||||
"history": "Historique du chat"
|
||||
}
|
||||
}
|
88
src/assets/locale/it/common.json
Normal file
88
src/assets/locale/it/common.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"pageAssist": "Page Assist",
|
||||
"selectAModel": "Seleziona un Modello",
|
||||
"save": "Salva",
|
||||
"saved": "Salvato",
|
||||
"cancel": "Annulla",
|
||||
"retry": "Riprova",
|
||||
"share": {
|
||||
"tooltip": {
|
||||
"share": "Condividi"
|
||||
},
|
||||
"modal": {
|
||||
"title": "Condividi Collegamento alla Chat"
|
||||
},
|
||||
"form": {
|
||||
"defaultValue": {
|
||||
"name": "Anonimo",
|
||||
"title": "Chat Senza Titolo"
|
||||
},
|
||||
"title": {
|
||||
"label": "Titolo della Chat",
|
||||
"placeholder": "Inserisci il Titolo della Chat",
|
||||
"required": "Titolo della Chat obbligatorio"
|
||||
},
|
||||
"name": {
|
||||
"label": "Il tuo Nome",
|
||||
"placeholder": "Inserisci il tuo Nome",
|
||||
"required": "Nome obbligatorio"
|
||||
},
|
||||
"btn": {
|
||||
"save": "Genera Link",
|
||||
"saving": "Sto generando il Link..."
|
||||
}
|
||||
},
|
||||
"notification": {
|
||||
"successGenerate": "Link copiato negli appunti",
|
||||
"failGenerate": "Impossibile generare il link"
|
||||
}
|
||||
},
|
||||
"copyToClipboard": "Copia negli Appunti",
|
||||
"webSearch": "Ricerca nel Web",
|
||||
"regenerate": "Rigenera",
|
||||
"edit": "Modifica",
|
||||
"saveAndSubmit": "Salva e Invia",
|
||||
"editMessage": {
|
||||
"placeholder": "Scrivi un messaggio..."
|
||||
},
|
||||
"submit": "Invia",
|
||||
"noData": "Nessun Dato",
|
||||
"noHistory": "Nessuna Cronologia Chat",
|
||||
"chatWithCurrentPage": "Chatta con la Pagina Corrente",
|
||||
"beta": "Beta",
|
||||
"tts": "Leggi ad Alta Voce",
|
||||
"currentChatModelSettings": "Impostazioni del Modello Corrente",
|
||||
"modelSettings": {
|
||||
"label": "Impostazioni del Modello",
|
||||
"description": "Imposta le opzioni del modello globalmente per tutte le chat",
|
||||
"form": {
|
||||
"keepAlive": {
|
||||
"label": "Keep Alive",
|
||||
"help": "Imposta il tempo per cui il modello deve rimanere caricato in memoria (default: 5m)",
|
||||
"placeholder": "Inserisci la durata del Keep Alive (e.g. 5m, 10m, 1h)"
|
||||
},
|
||||
"temperature": {
|
||||
"label": "Temperatura",
|
||||
"placeholder": "Inserisci la Temperatura (e.g. 0.7, 1.0)"
|
||||
},
|
||||
"numCtx": {
|
||||
"label": "Dimensione del Contesto",
|
||||
"placeholder": "Inserisci la Dimensione del Contesto (default: 2048)"
|
||||
},
|
||||
"seed": {
|
||||
"label": "Seed",
|
||||
"placeholder": "Inserisci il Valore Seed (e.g. 1234)",
|
||||
"help": "Riproducibilità dell'output del modello"
|
||||
},
|
||||
"topK": {
|
||||
"label": "Top K",
|
||||
"placeholder": "Inserisci il Valore Top K (e.g. 40, 100)"
|
||||
},
|
||||
"topP": {
|
||||
"label": "Top P",
|
||||
"placeholder": "Inserisci il Valore Top P (e.g. 0.9, 0.95)"
|
||||
}
|
||||
},
|
||||
"advanced": "Altre Impostazioni del Modello"
|
||||
}
|
||||
}
|
42
src/assets/locale/it/knowledge.json
Normal file
42
src/assets/locale/it/knowledge.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"addBtn": "Aggiungi nuova Knowledge Base",
|
||||
"columns": {
|
||||
"title": "Titolo",
|
||||
"status": "Stato",
|
||||
"embeddings": "Modello di Embedding",
|
||||
"createdAt": "Creato da",
|
||||
"action": "Azioni"
|
||||
},
|
||||
"expandedColumns": {
|
||||
"name": "Nome"
|
||||
},
|
||||
"tooltip": {
|
||||
"delete": "Elimina"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Sei sicuro di voler eliminare questa Knowledge Base?"
|
||||
},
|
||||
"deleteSuccess": "Knowledge Base eliminata correttamente",
|
||||
"status": {
|
||||
"pending": "In attesa",
|
||||
"finished": "Completato",
|
||||
"processing": "In corso"
|
||||
},
|
||||
"addKnowledge": "Aggiungi Knowledge Base",
|
||||
"form": {
|
||||
"title": {
|
||||
"label": "Titolo Knowledge Base",
|
||||
"placeholder": "Inserisci il titolo della Knowledge Base",
|
||||
"required": "Il Titolo è obbligatorio"
|
||||
},
|
||||
"uploadFile": {
|
||||
"label": "Carica File",
|
||||
"uploadText": "Trascina un file qui or scegli upload",
|
||||
"uploadHint": "Tipi di file supportati: .pdf, .csv, .txt, .md, .docx",
|
||||
"required": "File è obbligatorio"
|
||||
},
|
||||
"submit": "Invia",
|
||||
"success": "Knowledge Base aggiunta correttamente"
|
||||
},
|
||||
"noEmbeddingModel": "Aggiungi prima un modello dalla pagina di impostazione di Ollama"
|
||||
}
|
12
src/assets/locale/it/option.json
Normal file
12
src/assets/locale/it/option.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"newChat": "Nuova Chat",
|
||||
"selectAPrompt": "Scegli un Prompt",
|
||||
"githubRepository": "GitHub Repository",
|
||||
"settings": "Impsotazioni",
|
||||
"sidebarTitle": "Cronologia Chat",
|
||||
"error": "Errore",
|
||||
"somethingWentWrong": "Qualcosa è andato storto",
|
||||
"validationSelectModel": "Scegliere un modello per continuare",
|
||||
"deleteHistoryConfirmation": "Sei sicuro che vuoi eliminare la cronologia?",
|
||||
"editHistoryTitle": "Inserisci un nuovo titolo"
|
||||
}
|
29
src/assets/locale/it/playground.json
Normal file
29
src/assets/locale/it/playground.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"ollamaState": {
|
||||
"searching": "Sto cercando Ollama 🦙",
|
||||
"running": "Ollama è attivo 🦙",
|
||||
"notRunning": "Impossibile connettersi a Ollama 🦙",
|
||||
"connectionError": "C'è stato un problema di connessione. Controlla la <anchor>documentazione</anchor> per investigare."
|
||||
},
|
||||
"formError": {
|
||||
"noModel": "Seleziona un modello",
|
||||
"noEmbeddingModel": "Imposta un modello di embedding da Impostazioni > Ollama"
|
||||
},
|
||||
"form": {
|
||||
"textarea": {
|
||||
"placeholder": "Scrivi un messaggio..."
|
||||
},
|
||||
"webSearch": {
|
||||
"on": "Attivo",
|
||||
"off": "Disattivato"
|
||||
}
|
||||
},
|
||||
"tooltip": {
|
||||
"searchInternet": "Cerca su Internet",
|
||||
"speechToText": "Speech to Text",
|
||||
"uploadImage": "Carica immagine",
|
||||
"stopStreaming": "Ferma lo Streaming",
|
||||
"knowledge": "Conoscenza"
|
||||
},
|
||||
"sendWhenEnter": "Invia subito dopo Enter"
|
||||
}
|
286
src/assets/locale/it/settings.json
Normal file
286
src/assets/locale/it/settings.json
Normal file
@ -0,0 +1,286 @@
|
||||
{
|
||||
"generalSettings": {
|
||||
"title": "Impostazioni Generali",
|
||||
"settings": {
|
||||
"heading": "Impostazioni Web UI",
|
||||
"speechRecognitionLang": {
|
||||
"label": "Lingua per il riconoscimento vocale",
|
||||
"placeholder": "Scegli una lingua"
|
||||
},
|
||||
"language": {
|
||||
"label": "Lingua",
|
||||
"placeholder": "Scegli una lingua"
|
||||
},
|
||||
"darkMode": {
|
||||
"label": "Cambia il Tema",
|
||||
"options": {
|
||||
"light": "Chiaro",
|
||||
"dark": "Scuro"
|
||||
}
|
||||
},
|
||||
"copilotResumeLastChat": {
|
||||
"label": "Riprendi l'ultima chat quando apri il Pannello Laterale (Copilot)"
|
||||
},
|
||||
"hideCurrentChatModelSettings": {
|
||||
"label": "Nascondi le impostazioni correnti del modello Chat"
|
||||
}
|
||||
},
|
||||
"webSearch": {
|
||||
"heading": "Gestione ricerca Web",
|
||||
"searchMode": {
|
||||
"label": "Effettua ricerca web Internet semplice"
|
||||
},
|
||||
"provider": {
|
||||
"label": "Motori di ricerca",
|
||||
"placeholder": "Scegli un motore di ricerca"
|
||||
},
|
||||
"totalSearchResults": {
|
||||
"label": "Risultati della ricerca",
|
||||
"placeholder": "Inserisci il totale delle ricerche"
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"heading": "Impostazioni di Sistema",
|
||||
"deleteChatHistory": {
|
||||
"label": "Elimina cronologia Chat",
|
||||
"button": "Elimina",
|
||||
"confirm": "Sei sicuro che vuoi eliminare la tua cronologia delle chat? Questa azione non può essere annullata."
|
||||
},
|
||||
"export": {
|
||||
"label": "Esporta la cronologia Chat, Base di Conoscenza, e Prompts",
|
||||
"button": "Esporta Dati",
|
||||
"success": "Esportato con Successo"
|
||||
},
|
||||
"import": {
|
||||
"label": "Imposta la cronologia Chat, Base di Conoscenza, e Prompts",
|
||||
"button": "Importa Dati",
|
||||
"success": "Importato con Successo",
|
||||
"error": "Errore Importazione"
|
||||
}
|
||||
},
|
||||
"tts": {
|
||||
"heading": "Impostazioni Text-to-Speech",
|
||||
"ttsEnabled": {
|
||||
"label": "Abilita Text-to-Speech"
|
||||
},
|
||||
"ttsProvider": {
|
||||
"label": "Text-to-Speech Provider",
|
||||
"placeholder": "Seleziona un provider"
|
||||
},
|
||||
"ttsVoice": {
|
||||
"label": "Text-to-Speech Voce",
|
||||
"placeholder": "Seleziona una voce"
|
||||
},
|
||||
"ssmlEnabled": {
|
||||
"label": "Abilita SSML (Speech Synthesis Markup Language)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"manageModels": {
|
||||
"title": "Gestione Modelli",
|
||||
"addBtn": "Aggiungi un nuovo Modello",
|
||||
"columns": {
|
||||
"name": "Nome",
|
||||
"digest": "Digest",
|
||||
"modifiedAt": "Modificato il",
|
||||
"size": "Dimensioni",
|
||||
"actions": "Azioni"
|
||||
},
|
||||
"expandedColumns": {
|
||||
"parentModel": "Modello Padre",
|
||||
"format": "Formato",
|
||||
"family": "Famiglia",
|
||||
"parameterSize": "Numero di Parametri",
|
||||
"quantizationLevel": "Livello di Quantizzazione"
|
||||
},
|
||||
"tooltip": {
|
||||
"delete": "Elimina Modello",
|
||||
"repull": "Ri-Scarica Modello"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Sei sicuro di voler eliminare questo modello?",
|
||||
"repull": "Se sicuro che vuoi ri-scaricare questo modello?"
|
||||
},
|
||||
"modal": {
|
||||
"title": "Aggiungi Nuovo Modello",
|
||||
"placeholder": "Inserisci il Nome Modello",
|
||||
"pull": "Scarico del Modello"
|
||||
},
|
||||
"notification": {
|
||||
"pullModel": "Scarico del Modello",
|
||||
"pullModelDescription": "Scaricando il modello {{modelName}}. Per ulteriori dettagli visualizza l'icona dell'estensione.",
|
||||
"success": "Completato",
|
||||
"error": "Errore",
|
||||
"successDescription": "Scarico del modello completato",
|
||||
"successDeleteDescription": "Eliminazione del modello completato",
|
||||
"someError": "Qualcosa è andato storto. Riprova più tardi"
|
||||
}
|
||||
},
|
||||
"managePrompts": {
|
||||
"title": "Gestisci Prompts",
|
||||
"addBtn": "Aggiungi nuovo Prompt",
|
||||
"option1": "Normale",
|
||||
"option2": "RAG",
|
||||
"questionPrompt": "Question Prompt",
|
||||
"columns": {
|
||||
"title": "Titolo",
|
||||
"prompt": "Prompt",
|
||||
"type": "Tipo di Prompt",
|
||||
"actions": "Azioni"
|
||||
},
|
||||
"systemPrompt": "Prompt di Sistema",
|
||||
"quickPrompt": "Prompt Veloce",
|
||||
"tooltip": {
|
||||
"delete": "Elimina Prompt",
|
||||
"edit": "Modifica Prompt"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Sei sicuro di voler eliminare questo prompt? L'azione non può essere annullata."
|
||||
},
|
||||
"modal": {
|
||||
"addTitle": "Aggiungi Nuovo Prompt",
|
||||
"editTitle": "Modifica Prompt"
|
||||
},
|
||||
"form": {
|
||||
"title": {
|
||||
"label": "Titolo",
|
||||
"placeholder": "I Miei Prompt",
|
||||
"required": "Inserisci il Titolo"
|
||||
},
|
||||
"prompt": {
|
||||
"label": "Prompt",
|
||||
"placeholder": "Inserisci Prompt",
|
||||
"required": "Scrivi il prompt",
|
||||
"help": "Puoi usare {key} come variabile nel tuo prompt."
|
||||
},
|
||||
"isSystem": {
|
||||
"label": "Prompt di Sistema"
|
||||
},
|
||||
"btnSave": {
|
||||
"saving": "Aggiungendo Prompt...",
|
||||
"save": "Aggiungi Prompt"
|
||||
},
|
||||
"btnEdit": {
|
||||
"saving": "Aggiornando Prompt...",
|
||||
"save": "Aggiorna Prompt"
|
||||
}
|
||||
},
|
||||
"notification": {
|
||||
"addSuccess": "Prompt Aggiunto",
|
||||
"addSuccessDesc": "Il Prompt è stato aggiunto correttamente",
|
||||
"error": "Errore",
|
||||
"someError": "Qualcosa è andato storto. Riprova più tardi",
|
||||
"updatedSuccess": "Prompt Aggiornato",
|
||||
"updatedSuccessDesc": "Il Prompt è stato aggiornato correttmante",
|
||||
"deletedSuccess": "Prompt Eliminato",
|
||||
"deletedSuccessDesc": "Il Prompt è stato eliminato correttamente"
|
||||
}
|
||||
},
|
||||
"manageShare": {
|
||||
"title": "Gestione Condivisioni",
|
||||
"heading": "Configura l'URL della Pagina di Condivisione",
|
||||
"form": {
|
||||
"url": {
|
||||
"label": "URL Pagina di Condivisione",
|
||||
"placeholder": "Inserisci URL Pagina di Condivisione",
|
||||
"required": "Inserisci l'url della pagina di condivisione!",
|
||||
"help": "Per ragioni di privacy, tu puoi ospitare in self-host la paginacon il seguente URL. <anchor>Leggi altro</anchor>."
|
||||
}
|
||||
},
|
||||
"webshare": {
|
||||
"heading": "Condivisioni Web",
|
||||
"columns": {
|
||||
"title": "Titolo",
|
||||
"url": "URL",
|
||||
"actions": "Azioni"
|
||||
},
|
||||
"tooltip": {
|
||||
"delete": "Elimina Condivisione"
|
||||
},
|
||||
"confirm": {
|
||||
"delete": "Sei sicuro che vuoi eliminare questa condivisione? L'azione non può essere annullata."
|
||||
},
|
||||
"label": "Gestione Condivisioni",
|
||||
"description": "Abilita o Disattiva la funzionalità di condivisione"
|
||||
},
|
||||
"notification": {
|
||||
"pageShareSuccess": " URL di condivisione aggiornato correttamente",
|
||||
"someError": "Qualcosa è andato storto. Riprova più tardi",
|
||||
"webShareDeleteSuccess": "Condivisione eliminata correttamente"
|
||||
}
|
||||
},
|
||||
"ollamaSettings": {
|
||||
"title": "Impostazioni Ollama",
|
||||
"heading": "Configura Ollama",
|
||||
"settings": {
|
||||
"ollamaUrl": {
|
||||
"label": "Ollama URL",
|
||||
"placeholder": "Inserici l'URL di Ollama"
|
||||
},
|
||||
"ragSettings": {
|
||||
"label": "Impostazioni RAG",
|
||||
"model": {
|
||||
"label": "Modello di Embedding",
|
||||
"required": "Scegliere il modello",
|
||||
"help": "E' raccomandato l'uso di modelli come `nomic-embed-text`.",
|
||||
"placeholder": "Seleziona un modello"
|
||||
},
|
||||
"chunkSize": {
|
||||
"label": "Dimensione del Blocco (Chunk Size)",
|
||||
"placeholder": "Inserisci la Dimensione del Blocco (Chunk Size)",
|
||||
"required": "Inserisci la Dimensione del Blocco (chunk size)"
|
||||
},
|
||||
"chunkOverlap": {
|
||||
"label": "Sovrapposizione del Blocco (Chunk Overlap)",
|
||||
"placeholder": "Inserisci la Sovrapposizione del Blocco (Chunk Overlap)",
|
||||
"required": "Inserisci la Sovrapposizione del Blocco"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"label": "Configura il Prompt RAG",
|
||||
"option1": "Normale",
|
||||
"option2": "Web",
|
||||
"alert": "La configurazione del prompt di sistema qui è deprecato. Usa la sezione Gestione Prompt per aggiungere o modificare i prompts.Questa sezione sarà eliminata nelle prossime release",
|
||||
"systemPrompt": "Prompt di Sistema",
|
||||
"systemPromptPlaceholder": "Inserisci il Prompt di Sistema",
|
||||
"webSearchPrompt": "Prompt per la Ricerca Web",
|
||||
"webSearchPromptHelp": "Non rimuovere `{search_results}` dal prompt.",
|
||||
"webSearchPromptError": "Inserisci il prompt per la ricerca web",
|
||||
"webSearchPromptPlaceholder": "Imserosco il Prompt per la Ricerca Web",
|
||||
"webSearchFollowUpPrompt": "Prompt di Follow Up sulla Ricerca Web",
|
||||
"webSearchFollowUpPromptHelp": "Non rimuovere `{chat_history}` e `{question}` dal prompt.",
|
||||
"webSearchFollowUpPromptError": "Inserisci il Prompt di Follow Up della Ricerca Web!",
|
||||
"webSearchFollowUpPromptPlaceholder": "I tuoi Prompt di Follow Up delle Ricerche Web"
|
||||
},
|
||||
"advanced": {
|
||||
"label": "Configurazione Avanzata Ollama URL",
|
||||
"urlRewriteEnabled": {
|
||||
"label": "Abilita o Disabilita l'URL di Origine Personalizzato"
|
||||
},
|
||||
"rewriteUrl": {
|
||||
"label": "URL di Origine Personalizzato",
|
||||
"placeholder": "Inserisci URL di Origine Personalizzato"
|
||||
},
|
||||
"help": "Se hai problemi di connessione con Ollama su Page Assist, puoi configurare un URL di origine personalizzato. Per saperne di più sulla configurazione, <anchor>clicca qui</anchor>."
|
||||
}
|
||||
}
|
||||
},
|
||||
"manageSearch": {
|
||||
"title": "Gestisci Ricerca Web",
|
||||
"heading": "Configura Ricerca Web"
|
||||
},
|
||||
"about": {
|
||||
"title": "Informazioni",
|
||||
"heading": "Informazioni",
|
||||
"chromeVersion": "Versione di Page Assist",
|
||||
"ollamaVersion": "Versione di Ollama",
|
||||
"support": "Puoi supportare il progetto Page Assist donando o sponsorizzando attraverso le seguenti piattaforme:",
|
||||
"koFi": "Supporta su Ko-fi",
|
||||
"githubSponsor": "Sponsorizza su GitHub",
|
||||
"githubRepo": "Repository GitHub"
|
||||
},
|
||||
"manageKnowledge": {
|
||||
"title": "Gestisci Conoscenza",
|
||||
"heading": "Configura Base di Conoscenza"
|
||||
}
|
||||
}
|
7
src/assets/locale/it/sidepanel.json
Normal file
7
src/assets/locale/it/sidepanel.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"tooltip": {
|
||||
"embed": "L'inserimento della pagina potrebbe richiedere alcuni minuti. Attendere prego...",
|
||||
"clear": "Cancella la cronologia della chat",
|
||||
"history": "Cronologia della chat"
|
||||
}
|
||||
}
|
66
src/components/Common/CodeBlock.tsx
Normal file
66
src/components/Common/CodeBlock.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { Tooltip } from "antd"
|
||||
import { CheckIcon, ClipboardIcon } from "lucide-react"
|
||||
import { FC, memo, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||
import { coldarkDark } from "react-syntax-highlighter/dist/cjs/styles/prism"
|
||||
|
||||
interface Props {
|
||||
language: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export const CodeBlock: FC<Props> = memo(({ language, value }) => {
|
||||
const [isBtnPressed, setIsBtnPressed] = useState(false)
|
||||
const { t } = useTranslation("common")
|
||||
return (
|
||||
<>
|
||||
<div className="code relative text-base font-sans codeblock bg-zinc-950 rounded-md overflow-hidden">
|
||||
<div className="flex bg-gray-800 items-center justify-between py-1.5 px-4">
|
||||
<span className="text-xs lowercase text-gray-200">{language}</span>
|
||||
|
||||
<div className="flex items-center">
|
||||
<Tooltip title={t("copyToClipboard")}>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(value)
|
||||
setIsBtnPressed(true)
|
||||
setTimeout(() => {
|
||||
setIsBtnPressed(false)
|
||||
}, 4000)
|
||||
}}
|
||||
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-200 hover:bg-gray-700 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 focus:ring-offset-gray-100">
|
||||
{!isBtnPressed ? (
|
||||
<ClipboardIcon className="h-4 w-4" />
|
||||
) : (
|
||||
<CheckIcon className="h-4 w-4 text-green-400" />
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
language={language}
|
||||
style={coldarkDark}
|
||||
PreTag="div"
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
width: "100%",
|
||||
background: "transparent",
|
||||
padding: "1.5rem 1rem"
|
||||
}}
|
||||
lineNumberStyle={{
|
||||
userSelect: "none"
|
||||
}}
|
||||
codeTagProps={{
|
||||
style: {
|
||||
fontSize: "0.9rem",
|
||||
fontFamily: "var(--font-mono)"
|
||||
}
|
||||
}}>
|
||||
{value}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})
|
@ -1,68 +1,38 @@
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||
import remarkGfm from "remark-gfm"
|
||||
import { nightOwl } from "react-syntax-highlighter/dist/cjs/styles/prism"
|
||||
import remarkMath from "remark-math"
|
||||
import ReactMarkdown from "react-markdown"
|
||||
import ReactMarkdown, { Options } from "react-markdown"
|
||||
|
||||
import "property-information"
|
||||
import React from "react"
|
||||
import { Tooltip } from "antd"
|
||||
import { CheckIcon, ClipboardIcon } from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { FC, memo } from "react"
|
||||
import { CodeBlock } from "./CodeBlock"
|
||||
|
||||
export const MemoizedReactMarkdown: FC<Options> = memo(
|
||||
ReactMarkdown,
|
||||
(prevProps, nextProps) =>
|
||||
prevProps.children === nextProps.children &&
|
||||
prevProps.className === nextProps.className
|
||||
)
|
||||
|
||||
export default function Markdown({ message }: { message: string }) {
|
||||
const [isBtnPressed, setIsBtnPressed] = React.useState(false)
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ReactMarkdown
|
||||
<MemoizedReactMarkdown
|
||||
className="prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || "")
|
||||
return !inline ? (
|
||||
<div className="code relative text-base bg-gray-800 rounded-md overflow-hidden">
|
||||
<div className="flex items-center justify-between py-1.5 px-4">
|
||||
<span className="text-xs lowercase text-gray-200">
|
||||
{className && className.replace("language-", "")}
|
||||
</span>
|
||||
|
||||
<div className="flex items-center">
|
||||
<Tooltip title={t("copyToClipboard")}>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(children[0] as string)
|
||||
setIsBtnPressed(true)
|
||||
setTimeout(() => {
|
||||
setIsBtnPressed(false)
|
||||
}, 4000)
|
||||
}}
|
||||
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-200 hover:bg-gray-700 hover:text-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 focus:ring-offset-gray-100">
|
||||
{!isBtnPressed ? (
|
||||
<ClipboardIcon className="h-4 w-4" />
|
||||
) : (
|
||||
<CheckIcon className="h-4 w-4 text-green-400" />
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
{...props}
|
||||
children={String(children).replace(/\n$/, "")}
|
||||
style={nightOwl}
|
||||
key={Math.random()}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
fontSize: "1rem",
|
||||
lineHeight: "1.5rem"
|
||||
}}
|
||||
language={(match && match[1]) || ""}
|
||||
codeTagProps={{
|
||||
className: "text-sm"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<CodeBlock
|
||||
language={match ? match[1] : ""}
|
||||
value={String(children).replace(/\n$/, "")}
|
||||
/>
|
||||
) : (
|
||||
<code className={`${className} font-semibold`} {...props}>
|
||||
{children}
|
||||
@ -85,7 +55,7 @@ export default function Markdown({ message }: { message: string }) {
|
||||
}
|
||||
}}>
|
||||
{message}
|
||||
</ReactMarkdown>
|
||||
</MemoizedReactMarkdown>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ export const EditMessageForm = (props: Props) => {
|
||||
<div className="flex justify-center space-x-2 mt-2">
|
||||
<button
|
||||
aria-label={t("save")}
|
||||
className="bg-white dark:bg-black px-2.5 py-2 rounded-md text-gray-700 dark:text-gray-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-500 hover:bg-gray-100 dark:hover:bg-gray-900">
|
||||
className="bg-black px-2.5 py-2 rounded-md text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-gray-500 hover:bg-gray-900">
|
||||
{props.isBot ? t("save") : t("saveAndSubmit")}
|
||||
</button>
|
||||
<button
|
||||
|
221
src/components/Layouts/Header.tsx
Normal file
221
src/components/Layouts/Header.tsx
Normal file
@ -0,0 +1,221 @@
|
||||
import { useStorage } from "@plasmohq/storage/hook"
|
||||
import {
|
||||
BrainCog,
|
||||
ChevronLeft,
|
||||
CogIcon,
|
||||
ComputerIcon,
|
||||
GithubIcon,
|
||||
PanelLeftIcon,
|
||||
SquarePen,
|
||||
ZapIcon
|
||||
} from "lucide-react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useLocation, NavLink } from "react-router-dom"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { SelectedKnowledge } from "../Option/Knowledge/SelectedKnwledge"
|
||||
import { ModelSelect } from "../Common/ModelSelect"
|
||||
import { PromptSelect } from "../Common/PromptSelect"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { fetchChatModels } from "~/services/ollama"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import { Select, Tooltip } from "antd"
|
||||
import { getAllPrompts } from "@/db"
|
||||
import { ShareBtn } from "~/components/Common/ShareBtn"
|
||||
type Props = {
|
||||
setSidebarOpen: (open: boolean) => void
|
||||
setOpenModelSettings: (open: boolean) => void
|
||||
}
|
||||
|
||||
export const Header: React.FC<Props> = ({
|
||||
setOpenModelSettings,
|
||||
setSidebarOpen
|
||||
}) => {
|
||||
const { t } = useTranslation(["option", "common"])
|
||||
const [shareModeEnabled] = useStorage("shareMode", false)
|
||||
const [hideCurrentChatModelSettings] = useStorage(
|
||||
"hideCurrentChatModelSettings",
|
||||
false
|
||||
)
|
||||
const {
|
||||
selectedModel,
|
||||
setSelectedModel,
|
||||
clearChat,
|
||||
selectedSystemPrompt,
|
||||
setSelectedQuickPrompt,
|
||||
setSelectedSystemPrompt,
|
||||
messages,
|
||||
streaming
|
||||
} = useMessageOption()
|
||||
const {
|
||||
data: models,
|
||||
isLoading: isModelsLoading,
|
||||
isFetching: isModelsFetching
|
||||
} = useQuery({
|
||||
queryKey: ["fetchModel"],
|
||||
queryFn: () => fetchChatModels({ returnEmpty: true }),
|
||||
refetchInterval: 15000
|
||||
})
|
||||
|
||||
const { data: prompts, isLoading: isPromptLoading } = useQuery({
|
||||
queryKey: ["fetchAllPromptsLayout"],
|
||||
queryFn: getAllPrompts
|
||||
})
|
||||
|
||||
const { pathname } = useLocation()
|
||||
|
||||
const getPromptInfoById = (id: string) => {
|
||||
return prompts?.find((prompt) => prompt.id === id)
|
||||
}
|
||||
|
||||
const handlePromptChange = (value?: string) => {
|
||||
if (!value) {
|
||||
setSelectedSystemPrompt(undefined)
|
||||
setSelectedQuickPrompt(undefined)
|
||||
return
|
||||
}
|
||||
const prompt = getPromptInfoById(value)
|
||||
if (prompt?.is_system) {
|
||||
setSelectedSystemPrompt(prompt.id)
|
||||
} else {
|
||||
setSelectedSystemPrompt(undefined)
|
||||
setSelectedQuickPrompt(prompt!.content)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sticky top-0 z-[999] flex h-16 p-3 bg-gray-50 border-b dark:bg-[#171717] dark:border-gray-600">
|
||||
<div className="flex gap-2 items-center">
|
||||
{pathname !== "/" && (
|
||||
<div>
|
||||
<NavLink
|
||||
to="/"
|
||||
className="text-gray-500 items-center dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</NavLink>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<button
|
||||
className="text-gray-500 dark:text-gray-400"
|
||||
onClick={() => setSidebarOpen(true)}>
|
||||
<PanelLeftIcon className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
onClick={clearChat}
|
||||
className="inline-flex dark:bg-transparent bg-white items-center rounded-lg border dark:border-gray-700 bg-transparent px-3 py-2.5 text-xs lg:text-sm font-medium leading-4 text-gray-800 dark:text-white disabled:opacity-50 ease-in-out transition-colors duration-200 hover:bg-gray-100 dark:hover:bg-gray-800 dark:hover:text-white">
|
||||
<SquarePen className="h-5 w-5 " />
|
||||
<span className=" truncate ml-3">{t("newChat")}</span>
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-lg font-thin text-zinc-300 dark:text-zinc-600">
|
||||
{"/"}
|
||||
</span>
|
||||
<div className="hidden lg:block">
|
||||
<Select
|
||||
value={selectedModel}
|
||||
onChange={(e) => {
|
||||
setSelectedModel(e)
|
||||
localStorage.setItem("selectedModel", e)
|
||||
}}
|
||||
size="large"
|
||||
loading={isModelsLoading || isModelsFetching}
|
||||
filterOption={(input, option) =>
|
||||
option.label.key.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
showSearch
|
||||
placeholder={t("common:selectAModel")}
|
||||
className="w-72"
|
||||
options={models?.map((model) => ({
|
||||
label: (
|
||||
<span
|
||||
key={model.model}
|
||||
className="flex flex-row gap-3 items-center truncate">
|
||||
<OllamaIcon className="w-5 h-5" />
|
||||
<span className="truncate">{model.name}</span>
|
||||
</span>
|
||||
),
|
||||
value: model.model
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:hidden">
|
||||
<ModelSelect />
|
||||
</div>
|
||||
<span className="text-lg font-thin text-zinc-300 dark:text-zinc-600">
|
||||
{"/"}
|
||||
</span>
|
||||
<div className="hidden lg:block">
|
||||
<Select
|
||||
size="large"
|
||||
loading={isPromptLoading}
|
||||
showSearch
|
||||
placeholder={t("selectAPrompt")}
|
||||
className="w-60"
|
||||
allowClear
|
||||
onChange={handlePromptChange}
|
||||
value={selectedSystemPrompt}
|
||||
filterOption={(input, option) =>
|
||||
//@ts-ignore
|
||||
option.label.key.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
options={prompts?.map((prompt) => ({
|
||||
label: (
|
||||
<span
|
||||
key={prompt.title}
|
||||
className="flex flex-row gap-3 items-center">
|
||||
{prompt.is_system ? (
|
||||
<ComputerIcon className="w-4 h-4" />
|
||||
) : (
|
||||
<ZapIcon className="w-4 h-4" />
|
||||
)}
|
||||
{prompt.title}
|
||||
</span>
|
||||
),
|
||||
value: prompt.id
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:hidden">
|
||||
<PromptSelect />
|
||||
</div>
|
||||
<SelectedKnowledge />
|
||||
</div>
|
||||
<div className="flex flex-1 justify-end px-4">
|
||||
<div className="ml-4 flex items-center md:ml-6">
|
||||
<div className="flex gap-4 items-center">
|
||||
{!hideCurrentChatModelSettings && (
|
||||
<Tooltip title={t("common:currentChatModelSettings")}>
|
||||
<button
|
||||
onClick={() => setOpenModelSettings(true)}
|
||||
className="!text-gray-500 dark:text-gray-300 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<BrainCog className="w-6 h-6" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{pathname === "/" &&
|
||||
messages.length > 0 &&
|
||||
!streaming &&
|
||||
shareModeEnabled && <ShareBtn messages={messages} />}
|
||||
<Tooltip title={t("githubRepository")}>
|
||||
<a
|
||||
href="https://github.com/n4ze3m/page-assist"
|
||||
target="_blank"
|
||||
className="!text-gray-500 hidden lg:block dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<GithubIcon className="w-6 h-6" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("settings")}>
|
||||
<NavLink
|
||||
to="/settings"
|
||||
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<CogIcon className="w-6 h-6" />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,30 +1,12 @@
|
||||
import React, { useState } from "react"
|
||||
|
||||
import { useLocation, NavLink } from "react-router-dom"
|
||||
import { Sidebar } from "../Option/Sidebar"
|
||||
import { Drawer, Select, Tooltip } from "antd"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { fetchChatModels, getAllModels } from "~/services/ollama"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import {
|
||||
BrainCog,
|
||||
ChevronLeft,
|
||||
CogIcon,
|
||||
ComputerIcon,
|
||||
GithubIcon,
|
||||
PanelLeftIcon,
|
||||
SquarePen,
|
||||
ZapIcon
|
||||
} from "lucide-react"
|
||||
import { getAllPrompts } from "@/db"
|
||||
import { ShareBtn } from "~/components/Common/ShareBtn"
|
||||
import { Drawer } from "antd"
|
||||
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { SelectedKnowledge } from "../Option/Knowledge/SelectedKnwledge"
|
||||
import { useStorage } from "@plasmohq/storage/hook"
|
||||
import { ModelSelect } from "../Common/ModelSelect"
|
||||
import { PromptSelect } from "../Common/PromptSelect"
|
||||
|
||||
import { CurrentChatModelSettings } from "../Common/Settings/CurrentChatModelSettings"
|
||||
import { Header } from "./Header"
|
||||
|
||||
export default function OptionLayout({
|
||||
children
|
||||
@ -33,204 +15,16 @@ export default function OptionLayout({
|
||||
}) {
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false)
|
||||
const { t } = useTranslation(["option", "common"])
|
||||
const [shareModeEnabled] = useStorage("shareMode", false)
|
||||
const [openModelSettings, setOpenModelSettings] = useState(false)
|
||||
const [hideCurrentChatModelSettings] = useStorage(
|
||||
"hideCurrentChatModelSettings",
|
||||
false
|
||||
)
|
||||
|
||||
const {
|
||||
selectedModel,
|
||||
setSelectedModel,
|
||||
clearChat,
|
||||
selectedSystemPrompt,
|
||||
setSelectedQuickPrompt,
|
||||
setSelectedSystemPrompt,
|
||||
messages,
|
||||
streaming
|
||||
} = useMessageOption()
|
||||
|
||||
const {
|
||||
data: models,
|
||||
isLoading: isModelsLoading,
|
||||
isFetching: isModelsFetching
|
||||
} = useQuery({
|
||||
queryKey: ["fetchModel"],
|
||||
queryFn: () => fetchChatModels({ returnEmpty: true }),
|
||||
refetchInterval: 15000
|
||||
})
|
||||
|
||||
const { data: prompts, isLoading: isPromptLoading } = useQuery({
|
||||
queryKey: ["fetchAllPromptsLayout"],
|
||||
queryFn: getAllPrompts
|
||||
})
|
||||
|
||||
const { pathname } = useLocation()
|
||||
|
||||
const getPromptInfoById = (id: string) => {
|
||||
return prompts?.find((prompt) => prompt.id === id)
|
||||
}
|
||||
|
||||
const handlePromptChange = (value?: string) => {
|
||||
if (!value) {
|
||||
setSelectedSystemPrompt(undefined)
|
||||
setSelectedQuickPrompt(undefined)
|
||||
return
|
||||
}
|
||||
const prompt = getPromptInfoById(value)
|
||||
if (prompt?.is_system) {
|
||||
setSelectedSystemPrompt(prompt.id)
|
||||
} else {
|
||||
setSelectedSystemPrompt(undefined)
|
||||
setSelectedQuickPrompt(prompt!.content)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div className="flex flex-col">
|
||||
<div className="sticky top-0 z-[999] flex h-16 p-3 bg-gray-50 border-b dark:bg-[#171717] dark:border-gray-600">
|
||||
<div className="flex gap-2 items-center">
|
||||
{pathname !== "/" && (
|
||||
<div>
|
||||
<NavLink
|
||||
to="/"
|
||||
className="text-gray-500 items-center dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</NavLink>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<button
|
||||
className="text-gray-500 dark:text-gray-400"
|
||||
onClick={() => setSidebarOpen(true)}>
|
||||
<PanelLeftIcon className="w-6 h-6" />
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
onClick={clearChat}
|
||||
className="inline-flex dark:bg-transparent bg-white items-center rounded-lg border dark:border-gray-700 bg-transparent px-3 py-2.5 text-xs lg:text-sm font-medium leading-4 text-gray-800 dark:text-white disabled:opacity-50 ease-in-out transition-colors duration-200 hover:bg-gray-100 dark:hover:bg-gray-800 dark:hover:text-white">
|
||||
<SquarePen className="h-5 w-5 " />
|
||||
<span className=" truncate ml-3">{t("newChat")}</span>
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-lg font-thin text-zinc-300 dark:text-zinc-600">
|
||||
{"/"}
|
||||
</span>
|
||||
<div className="hidden lg:block">
|
||||
<Select
|
||||
value={selectedModel}
|
||||
onChange={(e) => {
|
||||
setSelectedModel(e)
|
||||
localStorage.setItem("selectedModel", e)
|
||||
}}
|
||||
size="large"
|
||||
loading={isModelsLoading || isModelsFetching}
|
||||
filterOption={(input, option) =>
|
||||
option.label.key
|
||||
.toLowerCase()
|
||||
.indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
showSearch
|
||||
placeholder={t("common:selectAModel")}
|
||||
className="w-64 "
|
||||
options={models?.map((model) => ({
|
||||
label: (
|
||||
<span
|
||||
key={model.model}
|
||||
className="flex flex-row gap-3 items-center">
|
||||
<OllamaIcon className="w-5 h-5" />
|
||||
{model.name}
|
||||
</span>
|
||||
),
|
||||
value: model.model
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:hidden">
|
||||
<ModelSelect />
|
||||
</div>
|
||||
<span className="text-lg font-thin text-zinc-300 dark:text-zinc-600">
|
||||
{"/"}
|
||||
</span>
|
||||
<div className="hidden lg:block">
|
||||
<Select
|
||||
size="large"
|
||||
loading={isPromptLoading}
|
||||
showSearch
|
||||
placeholder={t("selectAPrompt")}
|
||||
className="w-60"
|
||||
allowClear
|
||||
onChange={handlePromptChange}
|
||||
value={selectedSystemPrompt}
|
||||
filterOption={(input, option) =>
|
||||
//@ts-ignore
|
||||
option.label.key
|
||||
.toLowerCase()
|
||||
.indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
options={prompts?.map((prompt) => ({
|
||||
label: (
|
||||
<span
|
||||
key={prompt.title}
|
||||
className="flex flex-row gap-3 items-center">
|
||||
{prompt.is_system ? (
|
||||
<ComputerIcon className="w-4 h-4" />
|
||||
) : (
|
||||
<ZapIcon className="w-4 h-4" />
|
||||
)}
|
||||
{prompt.title}
|
||||
</span>
|
||||
),
|
||||
value: prompt.id
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:hidden">
|
||||
<PromptSelect />
|
||||
</div>
|
||||
<SelectedKnowledge />
|
||||
</div>
|
||||
<div className="flex flex-1 justify-end px-4">
|
||||
<div className="ml-4 flex items-center md:ml-6">
|
||||
<div className="flex gap-4 items-center">
|
||||
{!hideCurrentChatModelSettings && (
|
||||
<Tooltip title={t("common:currentChatModelSettings")}>
|
||||
<button
|
||||
onClick={() => setOpenModelSettings(true)}
|
||||
className="!text-gray-500 dark:text-gray-300 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<BrainCog className="w-6 h-6" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{pathname === "/" &&
|
||||
messages.length > 0 &&
|
||||
!streaming &&
|
||||
shareModeEnabled && <ShareBtn messages={messages} />}
|
||||
<Tooltip title={t("githubRepository")}>
|
||||
<a
|
||||
href="https://github.com/n4ze3m/page-assist"
|
||||
target="_blank"
|
||||
className="!text-gray-500 hidden lg:block dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<GithubIcon className="w-6 h-6" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("settings")}>
|
||||
<NavLink
|
||||
to="/settings"
|
||||
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
|
||||
<CogIcon className="w-6 h-6" />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main className="flex-1">{children}</main>
|
||||
</div>
|
||||
<>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Header
|
||||
setSidebarOpen={setSidebarOpen}
|
||||
setOpenModelSettings={setOpenModelSettings}
|
||||
/>
|
||||
<main className="flex-1">{children}</main>
|
||||
</div>
|
||||
|
||||
<Drawer
|
||||
@ -246,6 +40,6 @@ export default function OptionLayout({
|
||||
open={openModelSettings}
|
||||
setOpen={setOpenModelSettings}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
87
src/hooks/useScrollAnchor.tsx
Normal file
87
src/hooks/useScrollAnchor.tsx
Normal file
@ -0,0 +1,87 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
|
||||
export const useScrollAnchor = () => {
|
||||
const messagesRef = useRef<HTMLDivElement>(null)
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const visibilityRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [isAtBottom, setIsAtBottom] = useState(true)
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
|
||||
const scrollToBottom = useCallback(() => {
|
||||
if (messagesRef.current) {
|
||||
messagesRef.current.scrollIntoView({
|
||||
block: "end",
|
||||
behavior: "smooth"
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (messagesRef.current) {
|
||||
if (isAtBottom && !isVisible) {
|
||||
messagesRef.current.scrollIntoView({
|
||||
block: "end"
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [isAtBottom, isVisible])
|
||||
|
||||
useEffect(() => {
|
||||
const { current } = scrollRef
|
||||
|
||||
if (current) {
|
||||
const handleScroll = (event: Event) => {
|
||||
const target = event.target as HTMLDivElement
|
||||
const offset = 25
|
||||
const isAtBottom =
|
||||
target.scrollTop + target.clientHeight >= target.scrollHeight - offset
|
||||
console.log(target.scrollTop, target.clientHeight, target.scrollHeight)
|
||||
setIsAtBottom(isAtBottom)
|
||||
}
|
||||
|
||||
current.addEventListener("scroll", handleScroll, {
|
||||
passive: true
|
||||
})
|
||||
|
||||
return () => {
|
||||
current.removeEventListener("scroll", handleScroll)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (visibilityRef.current) {
|
||||
let observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
console.log(entry.isIntersecting)
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true)
|
||||
} else {
|
||||
setIsVisible(false)
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
rootMargin: "0px 0px -100px 0px"
|
||||
}
|
||||
)
|
||||
|
||||
observer.observe(visibilityRef.current)
|
||||
|
||||
return () => {
|
||||
observer.disconnect()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
messagesRef,
|
||||
scrollRef,
|
||||
visibilityRef,
|
||||
scrollToBottom,
|
||||
isAtBottom,
|
||||
isVisible
|
||||
}
|
||||
}
|
@ -2,10 +2,12 @@ import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import { en } from "./lang/en";
|
||||
import { pt } from "./lang/pt";
|
||||
import { fr } from "./lang/fr";
|
||||
import { ru } from "./lang/ru";
|
||||
import { ml } from "./lang/ml";
|
||||
import { zh } from "./lang/zh";
|
||||
import { ja } from "./lang/ja";
|
||||
import { it } from "./lang/it";
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
i18n
|
||||
@ -14,6 +16,8 @@ i18n
|
||||
.init({
|
||||
resources: {
|
||||
en: en,
|
||||
fr: fr,
|
||||
"it": it,
|
||||
ml: ml,
|
||||
"pt-BR": pt,
|
||||
"zh-CN": zh,
|
||||
|
15
src/i18n/lang/fr.ts
Normal file
15
src/i18n/lang/fr.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import option from "@/assets/locale/fr/option.json";
|
||||
import playground from "@/assets/locale/fr/playground.json";
|
||||
import common from "@/assets/locale/fr/common.json";
|
||||
import sidepanel from "@/assets/locale/fr/sidepanel.json";
|
||||
import settings from "@/assets/locale/fr/settings.json";
|
||||
import knowledge from "@/assets/locale/fr/knowledge.json";
|
||||
|
||||
export const fr = {
|
||||
option,
|
||||
playground,
|
||||
common,
|
||||
sidepanel,
|
||||
settings,
|
||||
knowledge
|
||||
}
|
15
src/i18n/lang/it.ts
Normal file
15
src/i18n/lang/it.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import option from "@/assets/locale/it/option.json";
|
||||
import playground from "@/assets/locale/it/playground.json";
|
||||
import common from "@/assets/locale/it/common.json";
|
||||
import sidepanel from "@/assets/locale/it/sidepanel.json";
|
||||
import settings from "@/assets/locale/it/settings.json";
|
||||
import knowledge from "@/assets/locale/it/knowledge.json";
|
||||
|
||||
export const it = {
|
||||
option,
|
||||
playground,
|
||||
common,
|
||||
sidepanel,
|
||||
settings,
|
||||
knowledge
|
||||
}
|
@ -4,6 +4,14 @@ export const supportLanguage = [
|
||||
label: "English",
|
||||
value: "en"
|
||||
},
|
||||
{
|
||||
label: "Français",
|
||||
value: "fr"
|
||||
},
|
||||
{
|
||||
label: "Italiano",
|
||||
value: "it"
|
||||
},
|
||||
{
|
||||
label: "Russian",
|
||||
value: "ru"
|
||||
|
@ -2,7 +2,8 @@ import { getKnowledgeById, updateKnowledgeStatus } from "@/db/knowledge"
|
||||
import { PageAssistPDFUrlLoader } from "@/loader/pdf-url"
|
||||
import {
|
||||
defaultEmbeddingChunkOverlap,
|
||||
defaultEmbeddingChunkSize
|
||||
defaultEmbeddingChunkSize,
|
||||
getOllamaURL
|
||||
} from "@/services/ollama"
|
||||
import { OllamaEmbeddings } from "@langchain/community/embeddings/ollama"
|
||||
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"
|
||||
@ -10,22 +11,14 @@ import { PageAssistVectorStore } from "./PageAssistVectorStore"
|
||||
import { PageAssisCSVUrlLoader } from "@/loader/csv"
|
||||
import { PageAssisTXTUrlLoader } from "@/loader/txt"
|
||||
import { PageAssistDocxLoader } from "@/loader/docx"
|
||||
import { cleanUrl } from "./clean-url"
|
||||
|
||||
const readAsArrayBuffer = (file: File): Promise<ArrayBuffer> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
resolve(reader.result as ArrayBuffer)
|
||||
}
|
||||
reader.onerror = reject
|
||||
reader.readAsArrayBuffer(file)
|
||||
})
|
||||
}
|
||||
|
||||
export const processKnowledge = async (msg: any, id: string): Promise<void> => {
|
||||
console.log(`Processing knowledge with id: ${id}`)
|
||||
try {
|
||||
const knowledge = await getKnowledgeById(id)
|
||||
const ollamaUrl = await getOllamaURL()
|
||||
|
||||
if (!knowledge) {
|
||||
console.error(`Knowledge with id ${id} not found`)
|
||||
@ -35,6 +28,7 @@ export const processKnowledge = async (msg: any, id: string): Promise<void> => {
|
||||
await updateKnowledgeStatus(id, "processing")
|
||||
|
||||
const ollamaEmbedding = new OllamaEmbeddings({
|
||||
baseUrl: cleanUrl(ollamaUrl),
|
||||
model: knowledge.embedding_model
|
||||
})
|
||||
const chunkSize = await defaultEmbeddingChunkSize()
|
||||
|
11
src/public/_locales/fr/messages.json
Normal file
11
src/public/_locales/fr/messages.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Page Assist - Une interface Web pour les modèles d'IA locaux"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Utilisez vos modèles d'IA locaux pour vous aider dans votre navigation web."
|
||||
},
|
||||
"openSidePanelToChat": {
|
||||
"message": "Ouvrir Copilot pour discuter"
|
||||
}
|
||||
}
|
11
src/public/_locales/it/messages.json
Normal file
11
src/public/_locales/it/messages.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Page Assist - Un'interfaccia web per modelli AI locali"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Usa i tuoi modelli AI in esecuzione locale per assisterti nella navigazione web."
|
||||
},
|
||||
"openSidePanelToChat": {
|
||||
"message": "Apri Copilot per chattare"
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ export default defineConfig({
|
||||
outDir: "build",
|
||||
|
||||
manifest: {
|
||||
version: "1.1.10",
|
||||
version: "1.1.11",
|
||||
name:
|
||||
process.env.TARGET === "firefox"
|
||||
? "Page Assist - A Web UI for Local AI Models"
|
||||
|
Loading…
x
Reference in New Issue
Block a user