diff --git a/.gitignore b/.gitignore index a08786b..1cd8458 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ keys.json # typescript .tsbuildinfo +.wxt \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0b8ea9e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,94 @@ + +# Contributing to Page Assist + +Thank you for your interest in contributing to Page Assist! We welcome contributions from anyone, whether it's reporting bugs, suggesting improvements, or submitting code changes. + +## Getting Started + +1. **Fork the repository** + + To start contributing, you'll need to fork the [Page Assist repository](https://github.com/n4ze3m/page-assist) by clicking the "Fork" button at the top right of the page. + +2. **Clone your forked repository** + + Once you have your own fork, clone it to your local machine: + + ``` + git clone https://github.com/YOUR-USERNAME/page-assist.git + ``` + +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: + + ``` + yarn install + ``` + +4. **Start the development server** + + To run the extension in development mode, use the following command: + + ``` + yarn dev + ``` + + This will open a browser window with the extension loaded. + +5. **Install Ollama locally** + + Page Assist requires [Ollama](https://ollama.ai) to be installed locally. Follow the installation instructions provided in the Ollama repository. + +## Making Changes + +Once you have the project set up locally, you can start making changes. We recommend creating a new branch for your changes: + +``` +git checkout -b my-feature-branch +``` + +Make your desired changes, and don't forget to add or update tests if necessary. + +## Submitting a Pull Request + +1. **Commit your changes** + + Once you've made your changes, commit them with a descriptive commit message: + + ``` + git commit -m "Add a brief description of your changes" + ``` + +2. **Push your changes** + + Push your changes to your forked repository: + + ``` + git push origin my-feature-branch + ``` + +3. **Open a Pull Request** + + Go to the original repository on GitHub and click the "New Pull Request" button. Select your forked repository and the branch you just pushed as the source, and the main repository's `main` branch as the destination. + +4. **Describe your changes** + + Provide a clear and concise description of the changes you've made, including any relevant issue numbers or other context. + +5. **Review and merge** + + The maintainers of the project will review your pull request and provide feedback or merge it if everything looks good. + +## Code Style and Guidelines + +To ensure consistency and maintainability, we follow certain code style guidelines. Please ensure your code adheres to these guidelines before submitting a pull request. + +- Use proper indentation and code formatting +- Write clear and concise comments when necessary +- Follow best practices for TypeScript and React development + +## Need Help? + +If you have any questions or need further assistance, feel free to open an issue or reach out to the maintainers. + +Thank you for your contribution! diff --git a/package.json b/package.json index e7e777c..eb13514 100644 --- a/package.json +++ b/package.json @@ -5,32 +5,40 @@ "description": "Use your locally running AI models to assist you in your web browsing.", "author": "n4ze3m", "scripts": { - "dev": "plasmo dev", - "build": "plasmo build", - "package": "plasmo package" + "dev": "wxt", + "dev:firefox": "wxt -b firefox", + "build": "wxt build", + "build:firefox": "wxt build -b firefox", + "zip": "wxt zip", + "zip:firefox": "wxt zip -b firefox", + "compile": "tsc --noEmit", + "postinstall": "wxt prepare" }, "dependencies": { "@ant-design/cssinjs": "^1.18.4", "@headlessui/react": "^1.7.18", "@heroicons/react": "^2.1.1", - "@langchain/community": "^0.0.21", - "@langchain/core": "^0.1.22", + "@langchain/community": "^0.0.41", "@mantine/form": "^7.5.0", "@mantine/hooks": "^7.5.3", "@plasmohq/storage": "^1.9.0", "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.10", "@tanstack/react-query": "^5.17.19", + "@vitejs/plugin-react": "^4.2.1", "antd": "^5.13.3", "axios": "^1.6.7", "dayjs": "^1.11.10", "html-to-text": "^9.0.5", - "langchain": "^0.1.9", + "i18next": "^23.10.1", + "i18next-browser-languagedetector": "^7.2.0", + "langchain": "^0.1.28", "lucide-react": "^0.350.0", - "plasmo": "0.84.1", + "pdfjs-dist": "^4.0.379", "property-information": "^6.4.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-i18next": "^14.1.0", "react-markdown": "8.0.0", "react-router-dom": "6.10.0", "react-syntax-highlighter": "^15.5.0", @@ -53,34 +61,11 @@ "postcss": "^8.4.33", "prettier": "3.2.4", "tailwindcss": "^3.4.1", - "typescript": "5.3.3" + "typescript": "5.3.3", + "vite-plugin-top-level-await": "^1.4.1", + "wxt": "^0.17.7" }, - "manifest": { - "host_permissions": [ - "http://*/*", - "https://*/*" - ], - "commands": { - "_execute_action": { - "suggested_key": { - "default": "Ctrl+Shift+L" - } - }, - "execute_side_panel": { - "description": "Open the side panel", - "suggested_key": { - "default": "Ctrl+Shift+P" - } - } - }, - "permissions": [ - "storage", - "activeTab", - "scripting", - "declarativeNetRequest", - "action", - "unlimitedStorage", - "contextMenus" - ] + "resolutions": { + "@langchain/core": "0.1.45" } } diff --git a/src/css/font.ttf b/src/assets/font.ttf similarity index 100% rename from src/css/font.ttf rename to src/assets/font.ttf diff --git a/assets/icon.png b/src/assets/icon.png similarity index 100% rename from assets/icon.png rename to src/assets/icon.png diff --git a/src/assets/locale/en/common.json b/src/assets/locale/en/common.json new file mode 100644 index 0000000..4626817 --- /dev/null +++ b/src/assets/locale/en/common.json @@ -0,0 +1,52 @@ +{ + "pageAssist": "Page Assist", + "selectAModel": "Select a Model", + "save": "Save", + "saved": "Saved", + "cancel": "Cancel", + "retry": "Retry", + "share": { + "tooltip": { + "share": "Share" + }, + "modal": { + "title": "Share link to Chat" + }, + "form": { + "defaultValue": { + "name": "Anonymous", + "title": "Untitled chat" + }, + "title": { + "label": "Chat title", + "placeholder": "Enter Chat title", + "required": "Chat title is required" + }, + "name": { + "label": "Your name", + "placeholder": "Enter your name", + "required": "Your name is required" + }, + "btn": { + "save": "Generate Link", + "saving": "Generating Link..." + } + }, + "notification": { + "successGenerate": "Link copied to clipboard", + "failGenerate": "Failed to generate link" + } + }, + "copyToClipboard": "Copy to clipboard", + "webSearch": "Searching the web", + "regenerate": "Regenerate", + "edit": "Edit", + "saveAndSubmit": "Save & Submit", + "editMessage": { + "placeholder": "Type a message..." + }, + "submit": "Submit", + "noData": "No data", + "noHistory": "No chat history", + "chatWithCurrentPage": "Chat with current page" +} \ No newline at end of file diff --git a/src/assets/locale/en/option.json b/src/assets/locale/en/option.json new file mode 100644 index 0000000..5739b40 --- /dev/null +++ b/src/assets/locale/en/option.json @@ -0,0 +1,12 @@ +{ + "newChat": "New Chat", + "selectAPrompt": "Select a Prompt", + "githubRepository": "GitHub Repository", + "settings": "Settings", + "sidebarTitle": "Chat History", + "error": "Error", + "somethingWentWrong": "Something went wrong", + "validationSelectModel": "Please select a model to continue", + "deleteHistoryConfirmation": "Are you sure you want to delete this history?", + "editHistoryTitle": "Enter a new title" +} \ No newline at end of file diff --git a/src/assets/locale/en/playground.json b/src/assets/locale/en/playground.json new file mode 100644 index 0000000..6a91280 --- /dev/null +++ b/src/assets/locale/en/playground.json @@ -0,0 +1,27 @@ +{ + "ollamaState": { + "searching": "Searching for Your Ollama ๐Ÿฆ™", + "running": "Ollama is running ๐Ÿฆ™", + "notRunning": "Unable to connect to Ollama ๐Ÿฆ™" + }, + "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": "Search Internet", + "speechToText": "Speech to Text", + "uploadImage": "Upload Image", + "stopStreaming": "Stop Streaming" + }, + "sendWhenEnter": "Send when Enter pressed" +} \ No newline at end of file diff --git a/src/assets/locale/en/settings.json b/src/assets/locale/en/settings.json new file mode 100644 index 0000000..40e5c58 --- /dev/null +++ b/src/assets/locale/en/settings.json @@ -0,0 +1,207 @@ +{ + "generalSettings": { + "title": "General Settings", + "heading": "Web UI Settings", + "settings": { + "speechRecognitionLang": { + "label": "Speech Recognition Language", + "placeholder": "Select a language" + }, + "language": { + "label": "Language", + "placeholder": "Select a language" + }, + "darkMode": { + "label": "Change Theme", + "options": { + "light": "Light", + "dark": "Dark" + } + }, + "searchMode": { + "label": "Perform Simple Internet Search" + }, + "deleteChatHistory": { + "label": "Delete Chat History", + "button": "Delete", + "confirm": "Are you sure you want to delete your chat history? This action cannot be undone." + } + } + }, + "manageModels": { + "title": "Manage Models", + "addBtn": "Add New Model", + "columns": { + "name": "Name", + "digest": "Digest", + "modifiedAt": "Modified At", + "size": "Size", + "actions": "Actions" + }, + "expandedColumns": { + "parentModel": "Parent Model", + "format": "Format", + "family": "Family", + "parameterSize": "Parameter Size", + "quantizationLevel": "Quantization Level" + }, + "tooltip": { + "delete": "Delete Model", + "repull": "Re-Pull Model" + }, + "confirm": { + "delete": "Are you sure you want to delete this model?", + "repull": "Are you sure you want to re-pull this model?" + }, + "modal": { + "title": "Add New Model", + "placeholder": "Enter Model Name", + "pull": "Pull Model" + }, + "notification": { + "pullModel": "Pulling Model", + "pullModelDescription": "Pulling {{modelName}} model. For more details, check the extension icon.", + "success": "Success", + "error": "Error", + "successDescription": "Successfully pulled the model", + "successDeleteDescription": "Successfully deleted the model", + "someError": "Something went wrong. Please try again later" + } + }, + "managePrompts": { + "title": "Manage Prompts", + "addBtn": "Add New Prompt", + "option1": "Normal", + "option2": "RAG", + "questionPrompt": "Question Prompt", + "columns": { + "title": "Title", + "prompt": "Prompt", + "type": "Prompt Type", + "actions": "Actions" + }, + "systemPrompt": "System Prompt", + "quickPrompt": "Quick Prompt", + "tooltip": { + "delete": "Delete Prompt", + "edit": "Edit Prompt" + }, + "confirm": { + "delete": "Are you sure you want to delete this prompt? This action cannot be undone." + }, + "modal": { + "addTitle": "Add New Prompt", + "editTitle": "Edit Prompt" + }, + "form": { + "title": { + "label": "Title", + "placeholder": "My Awesome Prompt", + "required": "Please enter a title" + }, + "prompt": { + "label": "Prompt", + "placeholder": "Enter Prompt", + "required": "Please enter a prompt", + "help": "You can use {key} as variable in your prompt." + }, + "isSystem": { + "label": "Is System Prompt" + }, + "btnSave": { + "saving": "Adding Prompt...", + "save": "Add Prompt" + }, + "btnEdit": { + "saving": "Updating Prompt...", + "save": "Update Prompt" + } + }, + "notification": { + "addSuccess": "Prompt Added", + "addSuccessDesc": "Prompt has been added successfully", + "error": "Error", + "someError": "Something went wrong. Please try again later", + "updatedSuccess": "Prompt Updated", + "updatedSuccessDesc": "Prompt has been updated successfully", + "deletedSuccess": "Prompt Deleted", + "deletedSuccessDesc": "Prompt has been deleted successfully" + } + }, + "manageShare": { + "title": "Manage Share", + "heading": "Configure Page Share URL", + "form": { + "url": { + "label": "Page Share URL", + "placeholder": "Enter Page Share URL", + "required": "Please input your Page Share URL!", + "help": "For privacy reasons, you can self-host the page share and provide the URL here. Learn More." + } + }, + "webshare": { + "heading": "Web Share", + "columns": { + "title": "Title", + "url": "URL", + "actions": "Actions" + }, + "tooltip": { + "delete": "Delete Share" + }, + "confirm": { + "delete": "Are you sure you want to delete this share? This action cannot be undone." + } + }, + "notification": { + "pageShareSuccess": "Page Share URL updated successfully", + "someError": "Something went wrong. Please try again later", + "webShareDeleteSuccess": "Web Share deleted successfully" + } + }, + "ollamaSettings": { + "title": "Ollama Settings", + "heading": "Configure Ollama", + "settings": { + "ollamaUrl": { + "label": "Ollama URL", + "placeholder": "Enter Ollama URL" + }, + "ragSettings": { + "label": "RAG Settings", + "model": { + "label": "Embedding Model", + "required": "Please select a model", + "help": "Highly recommended to use embedding models like `nomic-embed-text`.", + "placeholder": "Select a model" + }, + "chunkSize": { + "label": "Chunk Size", + "placeholder": "Enter Chunk Size", + "required": "Please enter a chunk size" + }, + "chunkOverlap": { + "label": "Chunk Overlap", + "placeholder": "Enter Chunk Overlap", + "required": "Please enter a chunk overlap" + } + }, + "prompt": { + "label": "Configure RAG Prompt", + "option1": "Normal", + "option2": "Web", + "alert": "Configuring the system prompt here is deprecated. Please use the Manage Prompts section to add or edit prompts. This section will be removed in a future release", + "systemPrompt": "System Prompt", + "systemPromptPlaceholder": "Enter System Prompt", + "webSearchPrompt": "Web Search Prompt", + "webSearchPromptHelp": "Do not remove `{search_results}` from the prompt.", + "webSearchPromptError": "Please enter a web search prompt", + "webSearchPromptPlaceholder": "Enter Web Search Prompt", + "webSearchFollowUpPrompt": "Web Search Follow Up Prompt", + "webSearchFollowUpPromptHelp": "Do not remove `{chat_history}` and `{question}` from the prompt.", + "webSearchFollowUpPromptError": "Please input your Web Search Follow Up Prompt!", + "webSearchFollowUpPromptPlaceholder": "Your Web Search Follow Up Prompt" + } + } + } +} \ No newline at end of file diff --git a/src/assets/locale/en/sidepanel.json b/src/assets/locale/en/sidepanel.json new file mode 100644 index 0000000..d46e213 --- /dev/null +++ b/src/assets/locale/en/sidepanel.json @@ -0,0 +1,5 @@ +{ + "tooltip": { + "embed": "It may take a few minutes to embed the page. Please wait..." + } +} \ No newline at end of file diff --git a/src/assets/locale/ml/common.json b/src/assets/locale/ml/common.json new file mode 100644 index 0000000..89f53c7 --- /dev/null +++ b/src/assets/locale/ml/common.json @@ -0,0 +1,52 @@ +{ + "pageAssist": "เดชเต‡เดœเต เด†เดธเดฟเดธเตเดฑเตเดฑเต", + "selectAModel": "เด’เดฐเต เดฎเต‹เดกเดฒเตโ€ เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•", + "save": "เดธเต‡เดตเต เดšเต†เดฏเตเดฏเตเด•", + "saved": "เดธเต‡เดตเต เดšเต†เดฏเตเดคเต", + "cancel": "เดฑเดฆเตเดฆเดพเด•เตเด•เตเด•", + "retry": "เดตเต€เดฃเตเดŸเตเด‚ เดถเตเดฐเดฎเดฟเด•เตเด•เตเด•", + "share": { + "tooltip": { + "share": "เดชเด™เตเด•เดฟเดŸเตเด•" + }, + "modal": { + "title": "เดšเดพเดฑเตเดฑเต เดฒเดฟเด™เตเด•เต เดชเด™เตเด•เดฟเดŸเตเด•" + }, + "form": { + "defaultValue": { + "name": "เด…เดœเตเดžเดพเดคเดจเตโ€", + "title": "เดชเต‡เดฐเดฟเดฒเตเดฒเดพเดคเตเดค เดšเดพเดฑเตเดฑเต" + }, + "title": { + "label": "เดšเดพเดฑเตเดฑเต เดคเดฒเด•เตเด•เต†เดŸเตเดŸเต", + "placeholder": "เดšเดพเดฑเตเดฑเต เดคเดฒเด•เตเด•เต†เดŸเตเดŸเต เดจเดฒเตเด•เตเด•", + "required": "เดšเดพเดฑเตเดฑเต เดคเดฒเด•เตเด•เต†เดŸเตเดŸเต เด†เดตเดถเตเดฏเดฎเดพเดฃเต" + }, + "name": { + "label": "เดจเดฟเด™เตเด™เดณเตเดŸเต† เดชเต‡เดฐเต", + "placeholder": "เดจเดฟเด™เตเด™เดณเตเดŸเต† เดชเต‡เดฐเต เดจเดฒเตเด•เตเด•", + "required": "เดจเดฟเด™เตเด™เดณเตเดŸเต† เดชเต‡เดฐเต เด†เดตเดถเตเดฏเดฎเดพเดฃเต" + }, + "btn": { + "save": "เดฒเดฟเด™เตเด•เต เดœเดจเดฑเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเด•", + "saving": "เดฒเดฟเด™เตเด•เต เดœเดจเดฑเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเดจเตเดจเต..." + } + }, + "notification": { + "successGenerate": "เดฒเดฟเด™เตเด•เต เด•เตเดฒเดฟเดชเตเดฌเต‹เดฐเตโ€เดกเดฟเดฒเต‡เด•เตเด•เต เดชเด•เดฐเตโ€เดคเตเดคเดฟ", + "failGenerate": "เดฒเดฟเด™เตเด•เต เดœเดจเดฑเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดฒเตโ€ เดชเดฐเดพเดœเดฏเดชเตเดชเต†เดŸเตเดŸเต" + } + }, + "copyToClipboard": "เด•เตเดฒเดฟเดชเตเดฌเต‹เดฐเตโ€เดกเดฟเดฒเต‡เด•เตเด•เต เดชเด•เดฐเตโ€เดคเตเดคเตเด•", + "webSearch": "เดตเต†เดฌเต เดคเดฟเดฐเดฏเตเดจเตเดจเต", + "regenerate": "เดตเต€เดฃเตเดŸเตเด‚ เดœเดจเดฑเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเด•", + "edit": "เดŽเดกเดฟเดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเด•", + "saveAndSubmit": "เดธเต‡เดตเต เดšเต†เดฏเตเดคเต เดธเดฎเดฐเตโ€เดชเตเดชเดฟเด•เตเด•เตเด•", + "editMessage": { + "placeholder": "เด’เดฐเต เดธเดจเตเดฆเต‡เดถเด‚ เดŸเตˆเดชเตเดชเต เดšเต†เดฏเตเดฏเตเด•..." + }, + "submit": "เดธเดฎเตผเดชเตเดชเดฟเด•เตเด•เตเด•", + "noData": "เดกเดพเดฑเตเดฑ เดฒเดญเตเดฏเดฎเดฒเตเดฒ", + "noHistory": "เดšเดพเดฑเตเดฑเต เดšเดฐเดฟเดคเตเดฐเด‚ เดฒเดญเตเดฏเดฎเดฒเตเดฒ", + "chatWithCurrentPage": "เดจเดฟเดฒเดตเดฟเดฒเต† เดชเต‡เดœเดฟเดจเตเดฎเดพเดฏเดฟ เดšเดพเดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเด•" +} \ No newline at end of file diff --git a/src/assets/locale/ml/option.json b/src/assets/locale/ml/option.json new file mode 100644 index 0000000..7a3523b --- /dev/null +++ b/src/assets/locale/ml/option.json @@ -0,0 +1,12 @@ +{ + "newChat": "เดชเตเดคเดฟเดฏ เดšเดพเดฑเตเดฑเต", + "selectAPrompt": "เด’เดฐเต เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•", + "githubRepository": "เด—เดฟเดฑเตเดฑเตเดนเดฌเต เดฑเต†เดชเตเดชเต‹เดธเดฟเดฑเตเดฑเดฑเดฟ", + "settings": "เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เดณเตโ€", + "sidebarTitle": "เดšเดพเดฑเตเดฑเต เดšเดฐเดฟเดคเตเดฐเด‚", + "error": "เดชเดฟเดถเด•เต", + "somethingWentWrong": "เดŽเดจเตเดคเต‹ เดคเต†เดฑเตเดฑเดพเดฏเดฟ", + "deleteHistoryConfirmation": "เดจเดฟเด™เตเด™เดณเตเดŸเต† เดšเดพเดฑเตเดฑเต เดšเดฐเดฟเดคเตเดฐเด‚ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฃเดฎเต†เดจเตเดจเต เดคเต€เตผเดšเตเดšเดฏเดพเดฃเต‹?", + "editHistoryTitle": "เดšเดพเดฑเตเดฑเต title เดŽเดกเดฟเดฑเตเดฑเตเดšเต†เดฏเตเดฏเตเด•", + "validationSelectModel": "เดคเตเดŸเดฐเตเดจเตเดจเดคเดฟเดจเต เด’เดฐเต เดฎเต‹เดกเดฒเตโ€ เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•" +} \ No newline at end of file diff --git a/src/assets/locale/ml/playground.json b/src/assets/locale/ml/playground.json new file mode 100644 index 0000000..691618b --- /dev/null +++ b/src/assets/locale/ml/playground.json @@ -0,0 +1,27 @@ +{ + "ollamaState": { + "searching": "เดจเดฟเด™เตเด™เดณเตเดŸเต† เด’เดฒเตเดฒเดพเดฎเดฏเตเด•เตเด•เดพเดฏเดฟ เดคเดฟเดฐเดฏเตเดจเตเดจเต ๐Ÿฆ™", + "running": "เด’เดฒเตเดฒเดพเดฎ เดชเตเดฐเดตเดฐเตโ€เดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเต ๐Ÿฆ™", + "notRunning": "เด’เดฒเตเดฒเดพเดฎเดฏเตเดฎเดพเดฏเดฟ เดฌเดจเตเดงเดฟเดชเตเดชเดฟเด•เตเด•เดพเดจเตโ€ เด•เดดเดฟเดฏเตเดจเตเดจเดฟเดฒเตเดฒ ๐Ÿฆ™" + }, + "formError": { + "noModel": "เดฆเดฏเดตเดพเดฏเดฟ เด’เดฐเต เดฎเต‹เดกเดฒเตโ€ เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•", + "noEmbeddingModel": "เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เดณเตโ€ > เด’เดฒเตเดฒเดพเดฎ เดชเต‡เดœเดฟเดฒเตเดณเตเดณ เดŽเด‚เดฌเต†เดกเดฟเด‚เด—เต เดฎเต‹เดกเดฒเตโ€ เดธเดœเตเดœเต€เด•เดฐเดฟเด•เตเด•เตเด•" + }, + "form": { + "textarea": { + "placeholder": "เด’เดฐเต เดธเดจเตเดฆเต‡เดถเด‚ เดŸเตˆเดชเตเดชเต เดšเต†เดฏเตเดฏเตเด•..." + }, + "webSearch": { + "on": "เด“เดฃเตโ€", + "off": "เด“เดซเต" + } + }, + "tooltip": { + "searchInternet": "เด‡เดจเตเดฑเดฐเตโ€เดจเต†เดฑเตเดฑเต เดคเดฟเดฐเดฏเตเด•", + "speechToText": "เดธเด‚เดญเดพเดทเดฃเด‚ เดŸเต†เด•เตเดธเตเดฑเตเดฑเดพเดฏเดฟ", + "uploadImage": "เด‡เดฎเต‡เดœเต เด…เดชเตโ€Œเดฒเต‹เดกเต เดšเต†เดฏเตเดฏเตเด•", + "stopStreaming": "เดธเตเดŸเตเดฐเต€เดฎเดฟเด‚เด—เต เดจเดฟเตผเดคเตเดคเตเด•" + }, + "sendWhenEnter": "เดŽเดจเตเดฑเดฐเตโ€ เด…เดฎเดฐเตโ€เดคเตเดคเตเดฎเตเดชเต‹เดณเตโ€ เด…เดฏเดฏเตเด•เตเด•เตเด•" +} \ No newline at end of file diff --git a/src/assets/locale/ml/settings.json b/src/assets/locale/ml/settings.json new file mode 100644 index 0000000..94f06af --- /dev/null +++ b/src/assets/locale/ml/settings.json @@ -0,0 +1,207 @@ +{ + "generalSettings": { + "title": "เดชเตŠเดคเตเดตเดพเดฏ เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เดณเตโ€", + "heading": "เดตเต†เดฌเต UI เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เดณเตโ€", + "settings": { + "speechRecognitionLang": { + "label": "เดธเด‚เดญเดพเดทเดฃ เดคเดฟเดฐเดฟเดšเตเดšเดฑเดฟเดฏเดฒเตโ€ เดญเดพเดท", + "placeholder": "เด’เดฐเต เดญเดพเดท เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•" + }, + "language": { + "label": "เดญเดพเดท", + "placeholder": "เด’เดฐเต เดญเดพเดท เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•" + }, + "darkMode": { + "label": "เดคเต€เด‚ เดฎเดพเดฑเตเดฑเตเด•", + "options": { + "light": "เดฒเตˆเดฑเตเดฑเต", + "dark": "เดกเดพเดฐเตโ€เด•เตเด•เต" + } + }, + "searchMode": { + "label": "เดธเดพเดงเดพเดฐเดฃ เด‡เดจเตเดฑเตผเดจเต†เดฑเตเดฑเต เด…เดจเตเดตเต‡เดทเดฃเด‚ เดจเดŸเดคเตเดคเตเด•" + }, + "deleteChatHistory": { + "label": "เดšเดพเดฑเตเดฑเต เดšเดฐเดฟเดคเตเดฐเด‚ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เตเด•", + "button": "เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เตเด•", + "confirm": "เดจเดฟเด™เตเด™เดณเตเดŸเต† เดšเดพเดฑเตเดฑเต เดšเดฐเดฟเดคเตเดฐเด‚ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฃเดฎเต†เดจเตเดจเต เดคเต€เตผเดšเตเดšเดฏเดพเดฃเต‹? เดˆ เดชเตเดฐเดตเตผเดคเตเดคเดจเด‚ เดชเดฟเดจเตเดจเต€เดŸเต เดชเดฟเตปเดตเดฒเดฟเด•เตเด•เดพเดจเดพเด•เดฟเดฒเตเดฒ." + } + } + }, + "manageModels": { + "title": "เดฎเต‹เดกเดฒเตเด•เดณเตโ€ เด•เตˆเด•เดพเดฐเตเดฏเด‚ เดšเต†เดฏเตเดฏเตเด•", + "addBtn": "เดชเตเดคเดฟเดฏ เดฎเต‹เดกเดฒเตโ€ เดšเต‡เดฐเตโ€เด•เตเด•เตเด•", + "columns": { + "name": "เดชเต‡เดฐเต", + "digest": "เดกเตˆเดœเดธเตเดฑเตเดฑเต", + "modifiedAt": "เด…เดตเดธเดพเดจเดฎเดพเดฏเดฟ เดชเดฐเดฟเดทเตโ€Œเด•เดฐเดฟเดšเตเดšเดคเต", + "size": "เดตเดฒเตเดชเตเดชเด‚", + "actions": "เดชเตเดฐเดตเตผเดคเตเดคเดจเด™เตเด™เตพ" + }, + "expandedColumns": { + "parentModel": "เดชเดพเดฐเดจเตเดฑเต เดฎเต‹เดกเดฒเตโ€", + "format": "เดซเต‹เดฐเตโ€เดฎเดพเดฑเตเดฑเต", + "family": "เด•เตเดŸเตเด‚เดฌเด‚", + "parameterSize": "เดชเดพเดฐเดพเดฎเต€เดฑเตเดฑเดฐเตโ€ เดตเดฒเตเดชเตเดชเด‚", + "quantizationLevel": "เด•เตเดตเดพเดฃเตเดŸเตˆเดธเต‡เดทเดจเตโ€ เดฒเต†เดตเดฒเตโ€" + }, + "tooltip": { + "delete": "เดฎเต‹เดกเดฒเตโ€ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เตเด•", + "repull": "เดฎเต‹เดกเดฒเตโ€ เดตเต€เดฃเตเดŸเตเด‚ เดฒเดญเตเดฏเดฎเดพเด•เตเด•เตเด•" + }, + "confirm": { + "delete": "เดˆ เดฎเต‹เดกเดฒเตโ€ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฃเดฎเต†เดจเตเดจเต เดคเต€เตผเดšเตเดšเดฏเดพเดฃเต‹?", + "repull": "เดˆ เดฎเต‹เดกเดฒเตโ€ เดตเต€เดฃเตเดŸเตเด‚ เดฒเดญเตเดฏเดฎเดพเด•เตเด•เดฃเดฎเต†เดจเตเดจเต เดคเต€เตผเดšเตเดšเดฏเดพเดฃเต‹?" + }, + "modal": { + "title": "เดชเตเดคเดฟเดฏ เดฎเต‹เดกเดฒเตโ€ เดšเต‡เดฐเตโ€เด•เตเด•เตเด•", + "placeholder": "เดฎเต‹เดกเดฒเตโ€ เดชเต‡เดฐเต เดจเดฒเตโ€เด•เตเด•", + "pull": "เดฎเต‹เดกเดฒเตโ€ เดฒเดญเตเดฏเดฎเดพเด•เตเด•เตเด•" + }, + "notification": { + "pullModel": "เดฎเต‹เดกเดฒเตโ€ เดฒเดญเตเดฏเดฎเดพเด•เตเด•เตเดจเตเดจเต", + "pullModelDescription": "{{modelName}} เดฎเต‹เดกเดฒเตโ€ เดฒเดญเตเดฏเดฎเดพเด•เตเด•เตเดจเตเดจเต. เด•เต‚เดŸเตเดคเดฒเตโ€ เดตเดฟเดตเดฐเด™เตเด™เดณเตโ€เด•เตเด•เดพเดฏเดฟ เดŽเด•เตโ€Œเดธเตเดฑเตเดฑเต†เดจเตโ€เดทเดจเตโ€ เดเด•เตเด•เดฃเตโ€ เดชเดฐเดฟเดถเต‹เดงเดฟเด•เตเด•เตเด•.", + "success": "เดตเดฟเดœเดฏเด‚", + "error": "เดชเดฟเดถเด•เต", + "successDescription": "เดฎเต‹เดกเดฒเตโ€ เดตเดฟเดœเดฏเด•เดฐเดฎเดพเดฏเดฟ เดฒเดญเตเดฏเดฎเดพเด•เตเด•เดฟ", + "successDeleteDescription": "เดฎเต‹เดกเดฒเตโ€ เดตเดฟเดœเดฏเด•เดฐเดฎเดพเดฏเดฟ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฟ", + "someError": "เดŽเดจเตเดคเต‹ เดคเต†เดฑเตเดฑเดพเดฏเดฟ. เดฆเดฏเดตเดพเดฏเดฟ เดชเดฟเดจเตเดจเต€เดŸเต เดตเต€เดฃเตเดŸเตเด‚ เดถเตเดฐเดฎเดฟเด•เตเด•เตเด•" + } + }, + "managePrompts": { + "title": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเตเด•เดณเตโ€ เด•เตˆเด•เดพเดฐเตเดฏเด‚ เดšเต†เดฏเตเดฏเตเด•", + "addBtn": "เดชเตเดคเดฟเดฏ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดšเต‡เดฐเตโ€เด•เตเด•เตเด•", + "columns": { + "title": "เดคเดฒเด•เตเด•เต†เดŸเตเดŸเต", + "prompt": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "type": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดคเดฐเด‚", + "actions": "เดชเตเดฐเดตเตผเดคเตเดคเดจเด™เตเด™เตพ" + }, + "option1": "เดธเดพเดงเดพเดฐเดฃ", + "option2": "RAG", + "questionPrompt": "เดšเต‹เดฆเตเดฏ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "systemPrompt": "เดธเดฟเดธเตเดฑเตเดฑเด‚ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "quickPrompt": "เดตเต‡เด—เดคเตเดคเดฟเดฒเตเดณเตเดณ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "tooltip": { + "delete": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เตเด•", + "edit": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดŽเดกเดฟเดฑเตเดฑเตเดšเต†เดฏเตเดฏเตเด•" + }, + "confirm": { + "delete": "เดˆ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฃเดฎเต†เดจเตเดจเต เดคเต€เตผเดšเตเดšเดฏเดพเดฃเต‹? เดˆ เดชเตเดฐเดตเตผเดคเตเดคเดจเด‚ เดชเดฟเดจเตเดจเต€เดŸเต เดชเดฟเตปเดตเดฒเดฟเด•เตเด•เดพเดจเดพเด•เดฟเดฒเตเดฒ." + }, + "modal": { + "addTitle": "เดชเตเดคเดฟเดฏ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดšเต‡เดฐเตโ€เด•เตเด•เตเด•", + "editTitle": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดŽเดกเดฟเดฑเตเดฑเตเดšเต†เดฏเตเดฏเตเด•" + }, + "form": { + "title": { + "label": "เดคเดฒเด•เตเด•เต†เดŸเตเดŸเต", + "placeholder": "เดŽเดจเตโ€เดฑเต† เด…เดคเตเดฒเตเดฏเดฎเดพเดฏ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "required": "เดฆเดฏเดตเดพเดฏเดฟ เด’เดฐเต เดคเดฒเด•เตเด•เต†เดŸเตเดŸเต เดจเดฒเตเด•เตเด•" + }, + "prompt": { + "label": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "placeholder": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดจเดฒเตเด•เตเด•", + "required": "เดฆเดฏเดตเดพเดฏเดฟ เด’เดฐเต เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดจเดฒเตเด•เตเด•", + "help": "เดจเดฟเด™เตเด™เดณเตโ€เด•เตเด•เต {key} เดŽเดจเตเดจ เดฐเต€เดคเดฟเดฏเดฟเดฒเตโ€ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเดฟเดฒเตโ€ เดตเต‡เดฐเดฟเดฏเดฌเดฟเดณเตเด•เดณเตโ€ เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เดพเดตเตเดจเตเดจเดคเดพเดฃเต." + }, + "isSystem": { + "label": "เดธเดฟเดธเตเดฑเตเดฑเด‚ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด†เดฃเต‹" + }, + "btnSave": { + "saving": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดšเต‡เดฐเตโ€เด•เตเด•เตเดจเตเดจเต...", + "save": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดšเต‡เดฐเตโ€เด•เตเด•เตเด•" + }, + "btnEdit": { + "saving": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเดจเตเดจเต...", + "save": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดฏเตเด•" + } + }, + "notification": { + "addSuccess": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดšเต‡เตผเดคเตเดคเต", + "addSuccessDesc": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดตเดฟเดœเดฏเด•เดฐเดฎเดพเดฏเดฟ เดšเต‡เตผเดคเตเดคเต", + "error": "เดชเดฟเดถเด•เต", + "someError": "เดŽเดจเตเดคเต‹ เดคเต†เดฑเตเดฑเดพเดฏเดฟ. เดฆเดฏเดตเดพเดฏเดฟ เดชเดฟเดจเตเดจเต€เดŸเต เดตเต€เดฃเตเดŸเตเด‚ เดถเตเดฐเดฎเดฟเด•เตเด•เตเด•", + "updatedSuccess": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตโ€Œเดคเต", + "updatedSuccessDesc": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดตเดฟเดœเดฏเด•เดฐเดฎเดพเดฏเดฟ เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดคเต", + "deletedSuccess": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฟ", + "deletedSuccessDesc": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดตเดฟเดœเดฏเด•เดฐเดฎเดพเดฏเดฟ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฟ" + } + }, + "manageShare": { + "title": "เดชเด™เตเด•เดฟเดŸเตเดจเตเดจเดคเต เด•เตˆเด•เดพเดฐเตเดฏเด‚ เดšเต†เดฏเตเดฏเตเด•", + "heading": "เดชเต‡เดœเต เดชเด™เตเด•เดฟเดŸเดพเดจเตเดณเตเดณ URL เด•เต‹เตบเดซเดฟเด—เตผ เดšเต†เดฏเตเดฏเตเด•", + "form": { + "url": { + "label": "เดชเต‡เดœเต เดชเด™เตเด•เดฟเดŸเดพเดจเตเดณเตเดณ URL", + "placeholder": "เดชเต‡เดœเต เดชเด™เตเด•เดฟเดŸเดพเดจเตเดณเตเดณ URL เดจเดฒเตเด•เตเด•", + "required": "เดฆเดฏเดตเดพเดฏเดฟ เดจเดฟเด™เตเด™เดณเตเดŸเต† เดชเต‡เดœเต เดชเด™เตเด•เดฟเดŸเดพเดจเตเดณเตเดณ URL เดจเดฒเตเด•เตเด•!", + "help": "เดธเตเดตเด•เดพเดฐเตเดฏเดคเด•เตเด•เดพเดฏเดฟ, เดจเดฟเด™เตเด™เดณเตโ€เด•เตเด•เต เดธเตเดตเดจเตเดคเดฎเดพเดฏเดฟ เดชเต‡เดœเต เดชเด™เตเด•เดฟเดŸเตเดจเตเดจ เดธเต—เด•เดฐเตเดฏเด‚ เดนเต‹เดธเตเดฑเตเดฑเต เดšเต†เดฏเตเดฏเดพเดจเตเด‚ เด…เดตเดฟเดŸเต†เดฏเตเดณเตเดณ URL เด‡เดตเดฟเดŸเต† เดจเดฒเตเด•เดพเดจเตเด‚ เด•เดดเดฟเดฏเตเด‚. เด•เต‚เดŸเตเดคเดฒเตโ€ เด…เดฑเดฟเดฏเตเด•." + } + }, + "webshare": { + "heading": "เดตเต†เดฌเต เดชเด™เตเด•เดฟเดŸเดฒเตโ€", + "columns": { + "title": "เดคเดฒเด•เตเด•เต†เดŸเตเดŸเต", + "url": "URL", + "actions": "เดชเตเดฐเดตเตผเดคเตเดคเดจเด™เตเด™เตพ" + }, + "tooltip": { + "delete": "เดชเด™เตเด•เดฟเดŸเดฒเตโ€ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เตเด•" + }, + "confirm": { + "delete": "เดˆ เดชเด™เตเด•เดฟเดŸเดฒเตโ€ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฃเดฎเต†เดจเตเดจเต เดคเต€เตผเดšเตเดšเดฏเดพเดฃเต‹? เดˆ เดชเตเดฐเดตเตผเดคเตเดคเดจเด‚ เดชเดฟเดจเตเดจเต€เดŸเต เดชเดฟเตปเดตเดฒเดฟเด•เตเด•เดพเดจเดพเด•เดฟเดฒเตเดฒ." + } + }, + "notification": { + "pageShareSuccess": "เดชเต‡เดœเต เดชเด™เตเด•เดฟเดŸเดพเดจเตเดณเตเดณ URL เดตเดฟเดœเดฏเด•เดฐเดฎเดพเดฏเดฟ เด…เดชเตเดกเต‡เดฑเตเดฑเต เดšเต†เดฏเตเดคเต", + "someError": "เดŽเดจเตเดคเต‹ เดคเต†เดฑเตเดฑเดพเดฏเดฟ. เดฆเดฏเดตเดพเดฏเดฟ เดชเดฟเดจเตเดจเต€เดŸเต เดตเต€เดฃเตเดŸเตเด‚ เดถเตเดฐเดฎเดฟเด•เตเด•เตเด•", + "webShareDeleteSuccess": "เดตเต†เดฌเต เดชเด™เตเด•เดฟเดŸเดฒเตโ€ เดตเดฟเดœเดฏเด•เดฐเดฎเดพเดฏเดฟ เด‡เดฒเตเดฒเดพเดคเดพเด•เตเด•เดฟ" + } + }, + "ollamaSettings": { + "title": "เด’เดฒเตเดฒเดพเดฎเดพ เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เดณเตโ€", + "heading": "เด’เดฒเตเดฒเดพเดฎเดพ เด•เต‹เตบเดซเดฟเด—เตผ เดšเต†เดฏเตเดฏเตเด•", + "settings": { + "ollamaUrl": { + "label": "เด’เดฒเตเดฒเดพเดฎเดพ URL", + "placeholder": "เด’เดฒเตเดฒเดพเดฎเดพ URL เดจเดฒเตเด•เตเด•" + }, + "ragSettings": { + "label": "RAG เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เดณเตโ€", + "model": { + "label": "เดŽเด‚เดฌเต†เดกเดฟเด‚เด—เต เดฎเต‹เดกเดฒเตโ€", + "required": "เดฆเดฏเดตเดพเดฏเดฟ เด’เดฐเต เดฎเต‹เดกเดฒเตโ€ เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•", + "help": "`nomic-embed-text` เดชเต‹เดฒเตเดณเตเดณ เดŽเด‚เดฌเต†เดกเดฟเด‚เด—เต เดฎเต‹เดกเดฒเตเด•เดณเตโ€ เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเดจเตเดจเดคเต เดตเดณเดฐเต† เดจเดจเตเดจเดพเดฏเดฟเดฐเดฟเด•เตเด•เตเด‚.", + "placeholder": "เด’เดฐเต เดฎเต‹เดกเดฒเตโ€ เดคเดฟเดฐเดžเตเดžเต†เดŸเตเด•เตเด•เตเด•" + }, + "chunkSize": { + "label": "เดšเด™เตเด•เต เดตเดฒเตเดชเตเดชเด‚", + "placeholder": "เดšเด™เตเด•เต เดตเดฒเตเดชเตเดชเด‚ เดจเดฒเตเด•เตเด•", + "required": "เดฆเดฏเดตเดพเดฏเดฟ เดšเด™เตเด•เต เดตเดฒเตเดชเตเดชเด‚ เดจเดฒเตเด•เตเด•" + }, + "chunkOverlap": { + "label": "เดšเด™เตเด•เต เด“เดตเดฐเตโ€เดฒเดพเดชเตเดชเต", + "placeholder": "เดšเด™เตเด•เต เด“เดตเดฐเตโ€เดฒเดพเดชเตเดชเต เดจเดฒเตเด•เตเด•", + "required": "เดฆเดฏเดตเดพเดฏเดฟ เดšเด™เตเด•เต เด“เดตเดฐเตโ€เดฒเดพเดชเตเดชเต เดจเดฒเตเด•เตเด•" + } + }, + "prompt": { + "label": "RAG เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด•เต‹เตบเดซเดฟเด—เตผ เดšเต†เดฏเตเดฏเตเด•", + "option1": "เดธเดพเดงเดพเดฐเดฃ", + "option2": "เดตเต†เดฌเต", + "alert": "เดธเดฟเดธเตเดฑเตเดฑเด‚ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เด‡เดตเดฟเดŸเต† เด•เต‹เตบเดซเดฟเด—เตผ เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเต เดชเดดเดฏเต—เด–เดฟเด•เดฎเดพเดฏเดฟ. เดฆเดฏเดตเดพเดฏเดฟ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเตเด•เดณเตโ€ เดšเต‡เดฐเตโ€เด•เตเด•เดพเดจเต‹ เดŽเดกเดฟเดฑเตเดฑเตเดšเต†เดฏเตเดฏเดพเดจเต‹ เดฎเดพเดจเต‡เดœเต เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเตโ€Œเดธเต เดธเต†เด•เตเดทเดจเตโ€ เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเด•. เดˆ เดธเต†เด•เตเดทเดจเตโ€ เดญเดพเดตเดฟเดฏเดฟเดฒเตโ€ เดจเต€เด•เตเด•เด‚ เดšเต†เดฏเตเดฏเดชเตเดชเต†เดŸเตเด‚.", + "systemPrompt": "เดธเดฟเดธเตเดฑเตเดฑเด‚ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "systemPromptPlaceholder": "เดธเดฟเดธเตเดฑเตเดฑเด‚ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดจเดฒเตเด•เตเด•", + "webSearchPrompt": "เดตเต†เดฌเต เดคเดฟเดฐเดฏเดฒเตโ€ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "webSearchPromptHelp": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเดฟเดฒเตโ€ เดจเดฟเดจเตเดจเต `{search_results}` เดจเต€เด•เตเด•เด‚ เดšเต†เดฏเตเดฏเดฐเตเดคเต.", + "webSearchPromptError": "เดฆเดฏเดตเดพเดฏเดฟ เด’เดฐเต เดตเต†เดฌเต เดคเดฟเดฐเดฏเดฒเตโ€ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดจเดฒเตเด•เตเด•", + "webSearchPromptPlaceholder": "เดตเต†เดฌเต เดคเดฟเดฐเดฏเดฒเตโ€ เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดจเดฒเตเด•เตเด•", + "webSearchFollowUpPrompt": "เดตเต†เดฌเต เดคเดฟเดฐเดฏเดฒเตโ€ เดคเตเดŸเดฐเตโ€เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต", + "webSearchFollowUpPromptHelp": "เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเดฟเดฒเตโ€ เดจเดฟเดจเตเดจเต `{chat_history}` เดฏเตเด‚ `{question}` เดฏเตเด‚ เดจเต€เด•เตเด•เด‚ เดšเต†เดฏเตเดฏเดฐเตเดคเต.", + "webSearchFollowUpPromptError": "เดฆเดฏเดตเดพเดฏเดฟ เดจเดฟเด™เตเด™เดณเตเดŸเต† เดตเต†เดฌเต เดคเดฟเดฐเดฏเดฒเตโ€ เดคเตเดŸเดฐเตโ€เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต เดจเดฒเตเด•เตเด•!", + "webSearchFollowUpPromptPlaceholder": "เดจเดฟเด™เตเด™เดณเตเดŸเต† เดตเต†เดฌเต เดคเดฟเดฐเดฏเดฒเตโ€ เดคเตเดŸเดฐเตโ€เดชเตเดฐเต‹เด‚เดชเตเดฑเตเดฑเต" + } + } + } +} \ No newline at end of file diff --git a/src/assets/locale/ml/sidepanel.json b/src/assets/locale/ml/sidepanel.json new file mode 100644 index 0000000..a94be53 --- /dev/null +++ b/src/assets/locale/ml/sidepanel.json @@ -0,0 +1,6 @@ +{ + "tooltip": { + "embed": "เดชเต‡เดœเต เดชเตเดฐเต‹เดธเดธเตเดธเต เดšเต†เดฏเตเดฏเตเดจเตเดจเดคเดฟเดจเต เด•เตเดฑเดšเตเดšเต เดฎเดฟเดจเดฟเดฑเตเดฑเตเด•เตพ เดŽเดŸเตเดคเตเดคเต‡เด•เตเด•เดพเด‚. เด•เดพเดคเตเดคเดฟเดฐเดฟเด•เตเด•เต‚.." + } + +} \ No newline at end of file diff --git a/src/css/tailwind.css b/src/assets/tailwind.css similarity index 100% rename from src/css/tailwind.css rename to src/assets/tailwind.css diff --git a/src/background.ts b/src/background.ts deleted file mode 100644 index 598d577..0000000 --- a/src/background.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { getOllamaURL, isOllamaRunning } from "~services/ollama" - -export {} - -const progressHuman = (completed: number, total: number) => { - return ((completed / total) * 100).toFixed(0) + "%" -} - -const clearBadge = () => { - chrome.action.setBadgeText({ text: "" }) - chrome.action.setTitle({ title: "" }) -} - -const streamDownload = async (url: string, model: string) => { - url += "/api/pull" - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ model, stream: true }) - }) - - const reader = response.body?.getReader() - - const decoder = new TextDecoder() - - let isSuccess = true - while (true) { - const { done, value } = await reader.read() - - if (done) { - break - } - - const text = decoder.decode(value) - try { - const json = JSON.parse(text.trim()) as { - status: string - total?: number - completed?: number - } - if (json.total && json.completed) { - chrome.action.setBadgeText({ - text: progressHuman(json.completed, json.total) - }) - chrome.action.setBadgeBackgroundColor({ color: "#0000FF" }) - } else { - chrome.action.setBadgeText({ text: "๐Ÿ‹๏ธโ€โ™‚๏ธ" }) - chrome.action.setBadgeBackgroundColor({ color: "#FFFFFF" }) - } - - chrome.action.setTitle({ title: json.status }) - - if (json.status === "success") { - isSuccess = true - } - } catch (e) { - console.error(e) - } - } - - if (isSuccess) { - chrome.action.setBadgeText({ text: "โœ…" }) - chrome.action.setBadgeBackgroundColor({ color: "#00FF00" }) - chrome.action.setTitle({ title: "Model pulled successfully" }) - } else { - chrome.action.setBadgeText({ text: "โŒ" }) - chrome.action.setBadgeBackgroundColor({ color: "#FF0000" }) - chrome.action.setTitle({ title: "Model pull failed" }) - } - - setTimeout(() => { - clearBadge() - }, 5000) -} - -chrome.runtime.onMessage.addListener(async (message) => { - if (message.type === "sidepanel") { - chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { - const tab = tabs[0] - await chrome.sidePanel.open({ - tabId: tab.id - }) - }) - } else if (message.type === "pull_model") { - const ollamaURL = await getOllamaURL() - - const isRunning = await isOllamaRunning() - - if (!isRunning) { - chrome.action.setBadgeText({ text: "E" }) - chrome.action.setBadgeBackgroundColor({ color: "#FF0000" }) - chrome.action.setTitle({ title: "Ollama is not running" }) - setTimeout(() => { - clearBadge() - }, 5000) - } - - await streamDownload(ollamaURL, message.modelName) - } -}) - -chrome.action.onClicked.addListener((tab) => { - chrome.tabs.create({ url: chrome.runtime.getURL("options.html") }) -}) - -chrome.commands.onCommand.addListener((command) => { - switch (command) { - case "execute_side_panel": - chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { - const tab = tabs[0] - await chrome.sidePanel.open({ - tabId: tab.id - }) - }) - break - default: - break - } -}) - -chrome.contextMenus.create({ - id: "open-side-panel-pa", - title: "Open Side Panel to Chat", - contexts: ["all"] -}) - -chrome.contextMenus.onClicked.addListener((info, tab) => { - if (info.menuItemId === "open-side-panel-pa") { - chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => { - const tab = tabs[0] - await chrome.sidePanel.open({ - tabId: tab.id - }) - }) - } -}) diff --git a/src/chain/chat-with-website.ts b/src/chain/chat-with-website.ts index 09e638d..3b909d4 100644 --- a/src/chain/chat-with-website.ts +++ b/src/chain/chat-with-website.ts @@ -14,7 +14,7 @@ import { RunnableMap, RunnableSequence, } from "langchain/schema/runnable"; -import type { ChatHistory } from "~store"; +import type { ChatHistory } from "~/store"; type RetrievalChainInput = { chat_history: string; question: string; diff --git a/src/components/Common/Markdown.tsx b/src/components/Common/Markdown.tsx index f3f374f..c4090e2 100644 --- a/src/components/Common/Markdown.tsx +++ b/src/components/Common/Markdown.tsx @@ -8,17 +8,11 @@ import "property-information" import React from "react" import { Tooltip } from "antd" import { CheckIcon, ClipboardIcon } from "lucide-react" +import { useTranslation } from "react-i18next" export default function Markdown({ message }: { message: string }) { const [isBtnPressed, setIsBtnPressed] = React.useState(false) - - React.useEffect(() => { - if (isBtnPressed) { - setTimeout(() => { - setIsBtnPressed(false) - }, 4000) - } - }, [isBtnPressed]) + const { t } = useTranslation("common") return ( @@ -37,11 +31,14 @@ export default function Markdown({ message }: { message: string }) {
- +
diff --git a/src/components/Common/Playground/Message.tsx b/src/components/Common/Playground/Message.tsx index abebce2..dfaeeb8 100644 --- a/src/components/Common/Playground/Message.tsx +++ b/src/components/Common/Playground/Message.tsx @@ -4,6 +4,7 @@ import { Image, Tooltip } from "antd" import { WebSearch } from "./WebSearch" import { CheckIcon, ClipboardIcon, Pen, RotateCcw } from "lucide-react" import { EditMessageForm } from "./EditMessageForm" +import { useTranslation } from "react-i18next" type Props = { message: string @@ -28,6 +29,8 @@ export const PlaygroundMessage = (props: Props) => { const [isBtnPressed, setIsBtnPressed] = React.useState(false) const [editMode, setEditMode] = React.useState(false) + const { t } = useTranslation("common") + return (
@@ -112,7 +115,8 @@ export const PlaygroundMessage = (props: Props) => { {props.isBot && ( <> {!props.hideCopy && ( - + ) } diff --git a/src/components/Common/ShareBtn.tsx b/src/components/Common/ShareBtn.tsx index 395b216..1dfdc5f 100644 --- a/src/components/Common/ShareBtn.tsx +++ b/src/components/Common/ShareBtn.tsx @@ -1,13 +1,14 @@ import { Form, Image, Input, Modal, Tooltip, message } from "antd" import { Share } from "lucide-react" import { useState } from "react" -import type { Message } from "~store/option" +import type { Message } from "~/store/option" import Markdown from "./Markdown" import React from "react" import { useMutation } from "@tanstack/react-query" -import { getPageShareUrl } from "~services/ollama" -import { cleanUrl } from "~libs/clean-url" -import { getUserId, saveWebshare } from "~libs/db" +import { getPageShareUrl } from "~/services/ollama" +import { cleanUrl } from "~/libs/clean-url" +import { getUserId, saveWebshare } from "~/libs/db" +import { useTranslation } from "react-i18next" type Props = { messages: Message[] @@ -75,6 +76,7 @@ export const PlaygroundMessage = ( } export const ShareBtn: React.FC = ({ messages }) => { + const { t } = useTranslation("common") const [open, setOpen] = useState(false) const [form] = Form.useForm() const name = Form.useWatch("name", form) @@ -104,7 +106,7 @@ export const ShareBtn: React.FC = ({ messages }) => { }) }) - if (!res.ok) throw new Error("Failed to create share link") + if (!res.ok) throw new Error(t("share.notification.failGenerate")) const data = await res.json() @@ -121,18 +123,23 @@ export const ShareBtn: React.FC = ({ messages }) => { onSuccess: async (data) => { const url = data.url navigator.clipboard.writeText(url) - message.success("Link copied to clipboard") - await saveWebshare({ title: data.title, url, api_url: data.api_url, share_id: data.share_id }) + message.success(t("share.notification.successGenerate")) + await saveWebshare({ + title: data.title, + url, + api_url: data.api_url, + share_id: data.share_id + }) setOpen(false) }, onError: (error) => { - message.error(error?.message || "Failed to create share link") + message.error(error?.message || t("share.notification.failGenerate")) } }) return ( <> - +
diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index d266710..26ac2a5 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -4,8 +4,8 @@ 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 { getAllModels } from "~services/ollama" -import { useMessageOption } from "~hooks/useMessageOption" +import { getAllModels } from "~/services/ollama" +import { useMessageOption } from "~/hooks/useMessageOption" import { ChevronLeft, CogIcon, @@ -15,8 +15,9 @@ import { SquarePen, ZapIcon } from "lucide-react" -import { getAllPrompts } from "~libs/db" -import { ShareBtn } from "~components/Common/ShareBtn" +import { getAllPrompts } from "~/libs/db" +import { ShareBtn } from "~/components/Common/ShareBtn" +import { useTranslation } from "react-i18next" export default function OptionLayout({ children @@ -24,6 +25,8 @@ export default function OptionLayout({ children: React.ReactNode }) { const [sidebarOpen, setSidebarOpen] = useState(false) + const { t } = useTranslation(["option", "common"]) + const { selectedModel, setSelectedModel, @@ -61,8 +64,8 @@ export default function OptionLayout({ if (prompt?.is_system) { setSelectedSystemPrompt(prompt.id) } else { - setSelectedQuickPrompt(prompt.content) - setSelectedSystemPrompt(null) + setSelectedQuickPrompt(prompt!.content) + setSelectedSystemPrompt("") } } @@ -93,7 +96,7 @@ export default function OptionLayout({ onClick={clearChat} className="inline-flex items-center rounded-lg border dark:border-gray-700 bg-transparent px-3 py-3 text-sm font-medium leading-4 text-gray-800 dark:text-white disabled:opacity-50 "> - New Chat + {t("newChat")}
@@ -106,12 +109,13 @@ export default function OptionLayout({ size="large" loading={isModelsLoading || isModelsFetching} filterOption={(input, option) => - option.label.toLowerCase().indexOf(input.toLowerCase()) >= + option!.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || - option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 + option!.value.toLowerCase().indexOf(input.toLowerCase()) >= + 0 } showSearch - placeholder="Select a model" + placeholder={t("common:selectAModel")} className="w-64 " options={models?.map((model) => ({ label: model.name, @@ -127,12 +131,13 @@ export default function OptionLayout({ size="large" loading={isPromptLoading} showSearch - placeholder="Select a prompt" + placeholder={t("selectAPrompt")} className="w-60" allowClear onChange={handlePromptChange} value={selectedSystemPrompt} filterOption={(input, option) => + //@ts-ignore option.label.key .toLowerCase() .indexOf(input.toLowerCase()) >= 0 @@ -161,7 +166,8 @@ export default function OptionLayout({ {pathname === "/" && messages.length > 0 && !streaming && ( )} - + - + @@ -185,14 +192,12 @@ export default function OptionLayout({ setSidebarOpen(false)} open={sidebarOpen}> - setSidebarOpen(false)} - /> + setSidebarOpen(false)} /> ) diff --git a/src/components/Layouts/SettingsOptionLayout.tsx b/src/components/Layouts/SettingsOptionLayout.tsx index a53bfcb..3b50161 100644 --- a/src/components/Layouts/SettingsOptionLayout.tsx +++ b/src/components/Layouts/SettingsOptionLayout.tsx @@ -5,6 +5,7 @@ import { Orbit, Share } from "lucide-react" +import { useTranslation } from "react-i18next" import { Link, useLocation } from "react-router-dom" function classNames(...classes: string[]) { @@ -25,7 +26,7 @@ const LinkComponent = (item: { item.current === item.href ? "bg-gray-100 text-gray-600 dark:bg-[#262626] dark:text-white" : "text-gray-700 hover:text-gray-600 hover:bg-gray-100 dark:text-gray-200 dark:hover:text-white dark:hover:bg-[#262626]", - "group flex gap-x-3 rounded-md py-2 pl-2 pr-3 text-sm leading-6 font-semibold" + "group flex gap-x-3 rounded-md py-2 pl-2 pr-3 text-sm font-semibold" )}> { const location = useLocation() + const { t } = useTranslation("settings") + return ( <>
@@ -54,31 +57,31 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => { className="flex gap-x-3 gap-y-1 whitespace-nowrap lg:flex-col"> - diff --git a/src/components/Option/Models/index.tsx b/src/components/Option/Models/index.tsx index e1d16e8..88f4804 100644 --- a/src/components/Option/Models/index.tsx +++ b/src/components/Option/Models/index.tsx @@ -1,18 +1,20 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { Skeleton, Table, Tag, Tooltip, notification, Modal, Input } from "antd" -import { bytePerSecondFormatter } from "~libs/byte-formater" -import { deleteModel, getAllModels } from "~services/ollama" +import { bytePerSecondFormatter } from "~/libs/byte-formater" +import { deleteModel, getAllModels } from "~/services/ollama" import dayjs from "dayjs" import relativeTime from "dayjs/plugin/relativeTime" import { useState } from "react" import { useForm } from "@mantine/form" import { Download, RotateCcw, Trash2 } from "lucide-react" +import { useTranslation } from "react-i18next" dayjs.extend(relativeTime) export const ModelsBody = () => { const queryClient = useQueryClient() const [open, setOpen] = useState(false) + const { t } = useTranslation(["settings", "common"]) const form = useForm({ initialValues: { @@ -32,22 +34,24 @@ export const ModelsBody = () => { queryKey: ["fetchAllModels"] }) notification.success({ - message: "Model Deleted", - description: "Model has been deleted successfully" + message: t("manageModels.notification.success"), + description: t("manageModels.notification.successDeleteDescription") }) }, onError: (error) => { notification.error({ message: "Error", - description: error?.message || "Something went wrong" + description: error?.message || t("manageModels.notification.someError") }) } }) const pullModel = async (modelName: string) => { notification.info({ - message: "Pulling Model", - description: `Pulling ${modelName} model. For more details, check the extension icon.` + message: t("manageModels.notification.pullModel"), + description: t("manageModels.notification.pullModelDescription", { + modelName + }) }) setOpen(false) @@ -76,7 +80,7 @@ export const ModelsBody = () => {
@@ -88,12 +92,12 @@ export const ModelsBody = () => { ( @@ -105,28 +109,26 @@ export const ModelsBody = () => { ) }, { - title: "Modified", + title: t("manageModels.columns.modifiedAt"), dataIndex: "modified_at", key: "modified_at", render: (text: string) => dayjs(text).fromNow(true) }, { - title: "Size", + title: t("manageModels.columns.size"), dataIndex: "size", key: "size", render: (text: number) => bytePerSecondFormatter(text) }, { - title: "Action", + title: t("manageModels.columns.actions"), render: (_, record) => (
- + - + diff --git a/src/components/Option/Playground/PlaygroundChat.tsx b/src/components/Option/Playground/PlaygroundChat.tsx index 4b42806..8b0a876 100644 --- a/src/components/Option/Playground/PlaygroundChat.tsx +++ b/src/components/Option/Playground/PlaygroundChat.tsx @@ -1,7 +1,7 @@ import React from "react" -import { useMessageOption } from "~hooks/useMessageOption" +import { useMessageOption } from "~/hooks/useMessageOption" import { PlaygroundEmpty } from "./PlaygroundEmpty" -import { PlaygroundMessage } from "~components/Common/Playground/Message" +import { PlaygroundMessage } from "~/components/Common/Playground/Message" export const PlaygroundChat = () => { const { diff --git a/src/components/Option/Playground/PlaygroundEmpty.tsx b/src/components/Option/Playground/PlaygroundEmpty.tsx index 8e6b1a8..81771da 100644 --- a/src/components/Option/Playground/PlaygroundEmpty.tsx +++ b/src/components/Option/Playground/PlaygroundEmpty.tsx @@ -1,14 +1,16 @@ import { useQuery } from "@tanstack/react-query" import { RotateCcw } from "lucide-react" import { useEffect, useState } from "react" +import { useTranslation } from "react-i18next" import { getOllamaURL, isOllamaRunning, setOllamaURL as saveOllamaURL -} from "~services/ollama" +} from "~/services/ollama" export const PlaygroundEmpty = () => { const [ollamaURL, setOllamaURL] = useState("") + const { t } = useTranslation(["playground", "common"]) const { data: ollamaInfo, status: ollamaStatus, @@ -40,7 +42,7 @@ export const PlaygroundEmpty = () => {

- Searching for Your Ollama ๐Ÿฆ™ + {t("ollamaState.searching")}

)} @@ -49,7 +51,7 @@ export const PlaygroundEmpty = () => {

- Ollama is running ๐Ÿฆ™ + {t("ollamaState.running")}

) : ( @@ -57,7 +59,7 @@ export const PlaygroundEmpty = () => {

- Unable to connect to Ollama ๐Ÿฆ™ + {t("ollamaState.notRunning")}

@@ -75,7 +77,7 @@ export const PlaygroundEmpty = () => { }} className="inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50 "> - Retry + {t("common:retry")}
) diff --git a/src/components/Option/Playground/PlaygroundForm.tsx b/src/components/Option/Playground/PlaygroundForm.tsx index 4f68c52..6776732 100644 --- a/src/components/Option/Playground/PlaygroundForm.tsx +++ b/src/components/Option/Playground/PlaygroundForm.tsx @@ -1,22 +1,24 @@ import { useForm } from "@mantine/form" import { useMutation, useQueryClient } from "@tanstack/react-query" import React from "react" -import useDynamicTextareaSize from "~hooks/useDynamicTextareaSize" -import { toBase64 } from "~libs/to-base64" -import { useMessageOption } from "~hooks/useMessageOption" +import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize" +import { toBase64 } from "~/libs/to-base64" +import { useMessageOption } from "~/hooks/useMessageOption" import { Checkbox, Dropdown, Switch, Tooltip } from "antd" import { Image } from "antd" -import { useSpeechRecognition } from "~hooks/useSpeechRecognition" -import { useWebUI } from "~store/webui" -import { defaultEmbeddingModelForRag } from "~services/ollama" +import { useSpeechRecognition } from "~/hooks/useSpeechRecognition" +import { useWebUI } from "~/store/webui" +import { defaultEmbeddingModelForRag } from "~/services/ollama" import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react" -import { getVariable } from "~utils/select-varaible" +import { getVariable } from "~/utils/select-varaible" +import { useTranslation } from "react-i18next" type Props = { dropedFile: File | undefined } export const PlaygroundForm = ({ dropedFile }: Props) => { + const { t } = useTranslation(["playground", "common"]) const inputRef = React.useRef(null) const [typing, setTyping] = React.useState(false) const { @@ -117,13 +119,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { }) const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Process" || e.key === "229" ) return + if (e.key === "Process" || e.key === "229") return if ( !typing && e.key === "Enter" && !e.shiftKey && !isSending && - sendWhenEnter + sendWhenEnter ) { e.preventDefault() form.onSubmit(async (value) => { @@ -131,16 +133,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { return } if (!selectedModel || selectedModel.length === 0) { - form.setFieldError("message", "Please select a model") + form.setFieldError("message", t("formError.noModel")) return } if (webSearch) { const defaultEM = await defaultEmbeddingModelForRag() if (!defaultEM) { - form.setFieldError( - "message", - "Please set an embedding model on the Settings > Ollama page" - ) + form.setFieldError("message", t("formError.noEmbeddingModel")) return } } @@ -181,16 +180,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
{ if (!selectedModel || selectedModel.length === 0) { - form.setFieldError("message", "Please select a model") + form.setFieldError("message", t("formError.noModel")) return } if (webSearch) { const defaultEM = await defaultEmbeddingModelForRag() if (!defaultEM) { - form.setFieldError( - "message", - "Please set an embedding model on the Settings > Ollama page" - ) + form.setFieldError("message", t("formError.noEmbeddingModel")) return } } @@ -223,12 +219,12 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { rows={1} style={{ minHeight: "60px" }} tabIndex={0} - placeholder="Type a message..." + placeholder={t("form.textarea.placeholder")} {...form.getInputProps("message")} />
- +
{ setWebSearch(e)} - checkedChildren="On" - unCheckedChildren="Off" + checkedChildren={t("form.webSearch.on")} + unCheckedChildren={t("form.webSearch.off")} />
- + - + - + {!isSending ? ( { onChange={(e) => setSendWhenEnter(e.target.checked) }> - Send when Enter pressed + {t("sendWhenEnter")} ) } @@ -340,11 +336,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { ) : null} - Submit + {t("common:submit")}
) : ( - + ) diff --git a/src/components/Option/Prompt/index.tsx b/src/components/Option/Prompt/index.tsx index 7945b37..9eac5d7 100644 --- a/src/components/Option/Prompt/index.tsx +++ b/src/components/Option/Prompt/index.tsx @@ -7,16 +7,18 @@ import { Modal, Input, Form, - Switch + Switch, + Empty } from "antd" import { Trash2, Pen, Computer, Zap } from "lucide-react" import { useState } from "react" +import { useTranslation } from "react-i18next" import { deletePromptById, getAllPrompts, savePrompt, updatePrompt -} from "~libs/db" +} from "~/libs/db" export const PromptBody = () => { const queryClient = useQueryClient() @@ -25,6 +27,7 @@ export const PromptBody = () => { const [editId, setEditId] = useState("") const [createForm] = Form.useForm() const [editForm] = Form.useForm() + const { t } = useTranslation("settings") const { data, status } = useQuery({ queryKey: ["fetchAllPrompts"], @@ -38,14 +41,14 @@ export const PromptBody = () => { queryKey: ["fetchAllPrompts"] }) notification.success({ - message: "Model Deleted", - description: "Model has been deleted successfully" + message: t("managePrompts.notification.deletedSuccess"), + description: t("managePrompts.notification.deletedSuccessDesc") }) }, onError: (error) => { notification.error({ - message: "Error", - description: error?.message || "Something went wrong" + message: t("managePrompts.notification.error"), + description: error?.message || t("managePrompts.notification.someError") }) } }) @@ -60,14 +63,15 @@ export const PromptBody = () => { setOpen(false) createForm.resetFields() notification.success({ - message: "Prompt Added", - description: "Prompt has been added successfully" + message: t("managePrompts.notification.addSuccess"), + description: t("managePrompts.notification.addSuccessDesc") }) }, onError: (error) => { notification.error({ - message: "Error", - description: error?.message || "Something went wrong" + message: t("managePrompts.notification.error"), + description: + error?.message || t("managePrompts.notification.someError") }) } }) @@ -87,14 +91,15 @@ export const PromptBody = () => { setOpenEdit(false) editForm.resetFields() notification.success({ - message: "Prompt Updated", - description: "Prompt has been updated successfully" + message: t("managePrompts.notification.updatedSuccess"), + description: t("managePrompts.notification.updatedSuccessDesc") }) }, onError: (error) => { notification.error({ - message: "Error", - description: error?.message || "Something went wrong" + message: t("managePrompts.notification.error"), + description: + error?.message || t("managePrompts.notification.someError") }) } }) @@ -108,7 +113,7 @@ export const PromptBody = () => {
@@ -120,43 +125,41 @@ export const PromptBody = () => {
is_system ? ( - - System Prompt + + {t("managePrompts.systemPrompt")} ) : ( - Quick Prompt + {t("managePrompts.quickPrompt")} ) }, { - title: "Action", + title: t("managePrompts.columns.actions"), render: (_, record) => (
- + - +
setOpen(false)} footer={null}> @@ -198,25 +201,35 @@ export const PromptBody = () => { form={createForm}> - + label={t("managePrompts.form.title.label")} + rules={[ + { + required: true, + message: t("managePrompts.form.title.required") + } + ]}> + + label={t("managePrompts.form.prompt.label")} + rules={[ + { + required: true, + message: t("managePrompts.form.prompt.required") + } + ]} + help={t("managePrompts.form.prompt.help")}> @@ -225,14 +238,16 @@ export const PromptBody = () => { setOpenEdit(false)} footer={null}> @@ -242,25 +257,35 @@ export const PromptBody = () => { form={editForm}> - + label={t("managePrompts.form.title.label")} + rules={[ + { + required: true, + message: t("managePrompts.form.title.required") + } + ]}> + + label={t("managePrompts.form.prompt.label")} + rules={[ + { + required: true, + message: t("managePrompts.form.prompt.required") + } + ]} + help={t("managePrompts.form.prompt.help")}> @@ -269,7 +294,9 @@ export const PromptBody = () => { diff --git a/src/components/Option/Settings/ollama.tsx b/src/components/Option/Settings/ollama.tsx index 08a73ca..e71045b 100644 --- a/src/components/Option/Settings/ollama.tsx +++ b/src/components/Option/Settings/ollama.tsx @@ -1,7 +1,7 @@ import { useMutation, useQuery } from "@tanstack/react-query" import { Form, InputNumber, Select, Skeleton } from "antd" import { useState } from "react" -import { SaveButton } from "~components/Common/SaveButton" +import { SaveButton } from "~/components/Common/SaveButton" import { defaultEmbeddingChunkOverlap, defaultEmbeddingChunkSize, @@ -10,18 +10,21 @@ import { getOllamaURL, saveForRag, setOllamaURL as saveOllamaURL -} from "~services/ollama" +} from "~/services/ollama" import { SettingPrompt } from "./prompt" +import { useTranslation } from "react-i18next" export const SettingsOllama = () => { const [ollamaURL, setOllamaURL] = useState("") + const { t } = useTranslation("settings") + const { data: ollamaInfo, status } = useQuery({ queryKey: ["fetchOllamURL"], queryFn: async () => { const [ollamaURL, allModels, chunkOverlap, chunkSize, defaultEM] = await Promise.all([ getOllamaURL(), - getAllModels({returnEmpty: true}), + getAllModels({ returnEmpty: true }), defaultEmbeddingChunkOverlap(), defaultEmbeddingChunkSize(), defaultEmbeddingModelForRag() @@ -54,7 +57,7 @@ export const SettingsOllama = () => {

- Configure Ollama + {t("ollamaSettings.heading")}

@@ -62,7 +65,7 @@ export const SettingsOllama = () => { { onChange={(e) => { setOllamaURL(e.target.value) }} - placeholder="Your Ollama URL" + placeholder={t("ollamaSettings.settings.ollamaUrl.placeholder")} className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100" />
@@ -88,7 +91,7 @@ export const SettingsOllama = () => {

- Configure RAG + {t("ollamaSettings.settings.ragSettings.label")}

@@ -108,18 +111,26 @@ export const SettingsOllama = () => { }}> + label={t("ollamaSettings.settings.ragSettings.model.label")} + help={t("ollamaSettings.settings.ragSettings.model.help")} + rules={[ + { + required: true, + message: t( + "ollamaSettings.settings.ragSettings.model.required" + ) + } + ]}> - option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || - option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 + option!.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || + option!.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 } onChange={(value) => { setSpeechToTextLanguage(value) @@ -44,7 +53,30 @@ export const SettingOther = () => { />
- Change Theme + + {t("generalSettings.settings.language.label")} + + +