├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── _locales ├── ar │ └── messages.json ├── da │ └── messages.json ├── de │ └── messages.json ├── el │ └── messages.json ├── en │ └── messages.json ├── es │ └── messages.json ├── fa │ └── messages.json ├── fi │ └── messages.json ├── fr │ └── messages.json ├── hy │ └── messages.json ├── id │ └── messages.json ├── is │ └── messages.json ├── it │ └── messages.json ├── ja │ └── messages.json ├── ko │ └── messages.json ├── nl │ └── messages.json ├── no │ └── messages.json ├── pl │ └── messages.json ├── pt │ └── messages.json ├── ru │ └── messages.json ├── sv │ └── messages.json ├── tr │ └── messages.json ├── uk │ └── messages.json ├── zh_cn │ └── messages.json └── zh_tw │ └── messages.json ├── build.js ├── crowdin.yml ├── cs_service_worker.js ├── defaultSearchEngines.json ├── html ├── addSearchEngineForPostRequest.html ├── addSearchEngineOrFolder.html ├── bookmark.html ├── bookmarkRemoval.html ├── bookmarks.html ├── history.html ├── options.html ├── popup.html ├── sidebar.html ├── subscription_choice.html └── subscription_status.html ├── icons ├── bookmark-grey-icon.svg ├── bookmark-red-icon.svg ├── context-search.svg ├── icon_128.png ├── icon_16.png ├── icon_20.png ├── icon_24.png ├── icon_256.png ├── icon_32.png ├── icon_48.png ├── icon_512.png ├── icon_64.png ├── search-icon.png ├── subscription-status-icon.png ├── uxwing-minus-red-icon.svg └── uxwing-plus-green-icon.svg ├── images └── searchEngineDescription.png ├── manifest.chrome.json ├── manifest.firefox.json ├── package-lock.json ├── package.json ├── scripts ├── addSearchEngineForPostRequest.js ├── addSearchEngineOrFolder.js ├── bookmark.js ├── bookmarkRemoval.js ├── bookmarks.js ├── constants.js ├── favicons.js ├── history.js ├── hosts.js ├── options.js ├── popup.js ├── selection.js ├── subscription_choice.js ├── subscription_status.js └── toggle_theme.js └── styles ├── addSearchEngineForPostRequest.css ├── addSearchEngineOrFolder.css ├── bookmark.css ├── bookmarkRemoval.css ├── dark_theme_toggle.css ├── options.css ├── popup.css └── sidebar.css /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_Store 3 | .eslintrc.json 4 | *.zip 5 | build/ 6 | config.json 7 | crowdin.yml 8 | node_modules/ 9 | web-ext-artifacts/ 10 | web-ext-config.js 11 | 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | ## End-User License Agreement (EULA) of Context Search 3 | This End-User License Agreement ("EULA") is a legal agreement between 4 | you and Olivier de Broqueville. 5 | 6 | This EULA agreement governs your acquisition and use of our Context 7 | Search software ("Software") directly from Olivier de 8 | Broqueville or indirectly through an authorised reseller 9 | or distributor (a "Reseller"). 10 | 11 | Please read this EULA agreement carefully before completing the 12 | installation process and using the Context Search software. 13 | It provides a license to use the Context Search software 14 | and contains warranty information and liability disclaimers. 15 | 16 | If you register for a free trial of the Context Search software, this EULA agreement will also govern that trial. By clicking "accept" or installing and/or using the Context Search software, you are confirming your acceptance of the Software and agreeing to become bound by the terms of this EULA agreement. 17 | 18 | If you are entering into this EULA agreement on behalf of a company or 19 | other legal entity, you represent that you have the authority to bind 20 | such entity and its affiliates to these terms and conditions. If you do 21 | not have such authority or if you do not agree with the terms and 22 | conditions of this EULA agreement, do not install or use the Software, 23 | and you must not accept this EULA agreement. 24 | 25 | This EULA agreement shall apply only to the Software supplied by 26 | Olivier de Broqueville herewith regardless of whether 27 | other software is referred to or described herein. The terms also apply 28 | to any Context Search updates, supplements, Internet-based 29 | services, and support services for the Software, unless other terms 30 | accompany those items on delivery. If so, those terms apply. 31 | 32 | ### License Grant 33 | Olivier de Broqueville hereby grants you a personal, 34 | non-transferable, non-exclusive licence to use the Context 35 | Search software on your devices in accordance with the terms 36 | of this EULA agreement. 37 | 38 | You are permitted to load the Context Search software (for 39 | example on a PC, laptop, mobile or tablet) under your control. You are 40 | responsible for ensuring your device meets the minimum requirements of 41 | the Context Search software. 42 | 43 | You are not permitted to: 44 | 45 | * Edit, alter, modify, adapt, translate or otherwise change the whole 46 | or any part of the Software nor permit the whole or any part of the 47 | Software to be combined with or become incorporated in any other 48 | software, nor decompile, disassemble or reverse engineer the 49 | Software or attempt to do any such things 50 | * Reproduce, copy, distribute, resell or otherwise use the Software 51 | for any commercial purpose 52 | * Allow any third party to use the Software on behalf of or for the 53 | benefit of any third party 54 | * Use the Software in any way which breaches any applicable local, 55 | national or international law 56 | * use the Software for any purpose that Olivier de Broqueville considers 57 | is a breach of this EULA agreement 58 | 59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 60 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 61 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 62 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 63 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 64 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 65 | IN THE SOFTWARE. 66 | 67 | ### Intellectual Property and Ownership 68 | Olivier de Broqueville shall at all times retain 69 | ownership of the Software as originally downloaded by you and all 70 | subsequent downloads of the Software by you. The Software (and the 71 | copyright, and other intellectual property rights of whatever nature in 72 | the Software, including any modifications made thereto) are and shall 73 | remain the property of Olivier de Broqueville. 74 | 75 | Olivier de Broqueville reserves the right to grant 76 | licences to use the Software to third parties. 77 | 78 | ### Termination 79 | This EULA agreement is effective from the date you first use the 80 | Software and shall continue until terminated. You may terminate it at 81 | any time upon written notice to Olivier de Broqueville. 82 | 83 | It will also terminate immediately if you fail to comply with any term 84 | of this EULA agreement. Upon such termination, the licenses granted by 85 | this EULA agreement will immediately terminate and you agree to stop all 86 | access and use of the Software. The provisions that by their nature 87 | continue and survive will survive any termination of this EULA 88 | agreement. 89 | 90 | ### Governing Law 91 | This EULA agreement, and any dispute arising out of or in connection 92 | with this EULA agreement, shall be governed by and construed in 93 | accordance with the laws of Switzerland. 94 | 95 | _______________________________________________________________________________________________________ 96 | 97 | The software uses Sortable under the MIT License (MIT) 98 | Copyright (c) 2019 All contributors to Sortable 99 | 100 | Permission is hereby granted, free of charge, to any person obtaining a copy 101 | of this software and associated documentation files (the "Software"), to deal 102 | in the Software without restriction, including without limitation the rights 103 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 104 | copies of the Software, and to permit persons to whom the Software is 105 | furnished to do so, subject to the following conditions: 106 | 107 | The above copyright notice and this permission notice shall be included in all 108 | copies or substantial portions of the Software. 109 | 110 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 111 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 112 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 113 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 114 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 115 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 116 | SOFTWARE. 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Context Search 2 | 3 | Firefox add-on to search selected text in a web page using your favorite search engines or your preferred AI chat engines and prompts. 4 | 5 | ## Features 6 | 7 | * Added dark theme for the Options page 8 | * Access search engines from the Context Menu 9 | * Access search engines from an Icons Grid 10 | * Compatible with search engines using either HTTP GET or HTTP POST requests 11 | * Reverse-image search using Google, Google Lens or TinEye 12 | * Site search 13 | * Carry out multiple searches at once using multisearch or folder multisearch 14 | * Manage your search engines from the extension's Options page 15 | * Many ways to easily add a new search engine 16 | * Support for search engines using OpenSearch description format 17 | * Simply add a search engine from mycroftproject.com 18 | * Use custom favicons for your search engines 19 | * Use custom prompts to question a selection of AI chatbots 20 | * Carry out searches from the Omnibox, i.e. url address bar 21 | * Assign keyboard shortcuts to your search engines 22 | * Export/Import your list of search engines as a safe backup 23 | 24 | 25 | ## Permissions 26 | 27 | **Notifications**, **Downloads**, **History**, **Bookmarks** and **Native Messaging** are optional permissions. They are disabled by default. If you'd like to: 28 | - show notifications and/or 29 | - save your list of search engines to your local disk, or 30 | - search your history or bookmarks from the Omnibox, or 31 | 32 | then open the extensions manager, select Context Search and, under the Permissions tab, enable the appropriate permissions. 33 | 34 | To open search results in a new private window, in the extensions manager, allow Context Search to "Run in Private Windows". 35 | 36 | ## How does it work 37 | 38 |
    39 |
  1. Select some text on a webpage
  2. 40 |
  3. Right click (or Alt-click or Alt+J for Chromium-based browsers or Ctrl+Shift+J for Firefox) on a selection
  4. 41 |
  5. A context menu (or a grid of icons) appears, displaying the list of search engines chosen in the extension's preferences
  6. 42 |
  7. Click on the search engine with which you’d like to search for the selected text
  8. 43 |
44 | 45 | The search results will appear as defined in the extension's preferences page. 46 | 47 | ## Managing search engines 48 | 49 | To manage your favorite search engines, you can go to the preferences page of Context Search. You can reach this page by opening the extensions page (Addon Manager) where all your add-ons are listed and then clicking on the "Preferences" button. 50 | 51 | ![How to define a search engine](images/searchEngineDescription.png) 52 | 53 | Please refer to the 4th screenshot above. 54 | 55 |
    56 |
  1. The checkbox at the start of a line determines whether the search engine should appear in the context menu.
  2. 57 |
  3. The next item on the line contains the name of the search engine and is followed by a keyword.
  4. 58 |
  5. This keyword is used in the url address bar (or omnibox) after the word “cs “ and before the search terms (e.g. to search for linux using the search engine Wikipedia, you would type: ‘cs w linux’, where w is the keyword assigned to Wikipedia).
  6. 59 |
  7. Next, you can assign a keyboard shortcut to a search engine to perform a quick search. Please note that not all key combinations will work as some may be reserved by the browser or your system.
  8. 60 |
  9. The second checkbox specifies whether you’d like to use the search engine in a “multi-search”. A “multi-search” is a search performed using multiple search engines and can be selected in the context menu of in the grid of icons.
  10. 61 |
  11. The checkbox is followed by the search query string. This is the generic url you would use to perform a search. Search query strings may contain the parameters %s or {searchTerms} where you'd like your search terms, i.e. the selected text, to appear.
  12. 62 |
  13. Click on and drag the move icon to the left of the trash icon to move each search engine up or down in the list.
  14. 63 |
  15. Click on the trash icon to remove a search engine from the list.
  16. 64 |
65 | 66 | The 'Reset' button will re-load the default list of search engines and their associated favicons. 67 | 68 | You can also import a JSON file containing your own list of search engines. It is strongly recommended to export your customized list of search engines as a backup in case anything goes wrong. 69 | 70 | ## How to add a search engine to your custom list of search engines 71 | 72 | * visit mycroftproject.com and click on the Context Search icon that appears before the textual link of a listed search engine 73 | * use the page action (i.e. Context Search icon in the url address bar) to add a search engine if the website supports open search 74 | * add a search engine manually via the Options page (you can test the query string before adding the search engine) 75 | * to add a search engine that uses a submit form via an HTTP POST request, double click in the website's search text box, then at least enter a search engine name in the dialog box that appears 76 | 77 | [Here](https://youtu.be/_kV7JCgGQLk) is a video demonstrating the different ways that you can add a search engine to yout custom list of search engines. 78 | 79 | ## How to add an AI prompt to your custom list of search engines 80 | 81 | For trouble-free use, **it is recommended that you log in** prior to using an AI provider. This is required when using Claude, Google AI Studio, Poe and You. At the bottom of the Options page, select the tab to 'Add a new AI prompt'. Chose the AI provider you'd like to use, add a name for your prompt and enter your prompt by inserting %s where you'd like your text selection to appear (e.g. 'Comprehensively explain the following for a 10 year old: %s'). 82 | 83 | In most cases, after selecting text on a web page and selecting your prompt in the context menu, a new tab will open with the relevant AI engine. When possible, the prompt will be pasted in the search box and automatically submitted to the AI engine, except for Poe so that users can choose their preferred large language model. The search results should then appear. 84 | 85 | N.B.: If the prompt is missing from the text area, you can paste the contents of the clipboard to which your prompt will have been copied. For this to work, you have to enable the Clipboard permission in the extension's preferences. Alternatively, it is possible to use Chatgpt's AI engines (GPT-4o) on Poe. 86 | 87 | ## How to add a separator (horizontal line) in the context menu 88 | 89 | At the bottom of the Options page, simply click on the "Add separator" button. This will add a separator to the bottom of your search engines list. Then, use the arrow handle to the right of the horizontal line to move the separator to the position where you would like it to be. The separator should appear in the context menu. 90 | 91 | ## How to add a custom favicon for a search engine 92 | 93 | From the Options page, click on the favicon right before the search engine's name. A popup window will open displaying the current favicon and the associated base64 string. Drag & drop a new image onto the existing one, then click on the 'Save' button for your changes to take effect. The popup will automatically close after you click on the 'Save' button. 94 | 95 | ## How to perform an AI search using the command window 96 | 97 | Prior to using this feature, **ensure that you have logged in** to the website(s) of the AI engine(s) that you'd like to use. This is required when using ChatGPT, Calude, Google AI Studio and Poe. 'ALT+K' for Chromium-based browsers or 'Ctrl+Shift+K' for Firefox opens a command window from which to carry out a direct AI search. Start by typing the keyword corresponding to the AI engine that you'd like to use followed by a 'Space' character. The current list of valid keywords is: 98 | 99 | - chatgpt 100 | - claude 101 | - gemini (for Google AI Studio where different models are available) 102 | - grok 103 | - perplexity 104 | - poe (where you can choose amongst different LL models, namely Llama 3.1) 105 | - andi 106 | - you 107 | 108 | If the AI engine is recognized, then it will automatically be styled as a tag. You can then continue typing your prompt completed by 'Enter'. The command window should then close and the search results be displayed. Unless you are using poe, then an additional step is required: select the LLM and submit the prompt. 109 | 110 | ## How to perform a search in the omnibox 111 | 112 | In the omnibox (or url address bar), type 'cs ' (without the quotes, and where cs stands for Context Search) followed by the keyword you have chosen for your seaarch engine in the extension's preferences, e.g. 'w ' (again without quotes) for Wikipedia, followed by your search term(s). The dot ('.'), the exclamation mark ('!'), '!h' or 'history' and '!b' or 'bookmarks' are reserved keywords. If the same keyword is used for different search engines, then a multi-search will be performed. 113 | 114 | Here are some examples: 115 | 116 | **cs w atom** 117 | will search for the word 'atom' in Wikipedia. 118 | 119 | **cs .** 120 | will open the Options page 121 | 122 | **cs ! cold fusion** 123 | will perform a multi-search for the search terms 'cold fusion' 124 | 125 | **cs !h** or **cs history** 126 | will display all your history 127 | 128 | **cs !b Mozilla** or **cs bookmarks Mozilla** 129 | will display all bookmarks that include the term Mozilla 130 | 131 | **cs !b recent** or **cs bookmarks recent** 132 | will display your 10 most recent bookmarks 133 | 134 | Please note that permissions for History and/or Bookmarks need to be anabled for the latter features to work. 135 | 136 | 137 | ## The main structure of a JSON file containing the search engines 138 | 139 | ```javascript 140 | { 141 | "id": { 142 | "index": 0, 143 | "name": "search engine's name", 144 | "keyword": "keyword to be used in an omnibox search", 145 | "keyboardShortcut": "keyboard shortcut assigned to the search engine", 146 | "multitab": "takes the value true or false depending on whether this search engine should be included in a multi-search or not", 147 | "url": "search engine query string (without the search terms)", 148 | "show": "takes the value true if the search engine is to be shown in the context menu or false if not", 149 | "base64": "a base 64 string representation of the search engine's favicon" 150 | } 151 | } 152 | ``` 153 | 154 | Here is an example of a JSON file containing 3 search engines: 155 | 156 | ```javascript 157 | { 158 | "bing": { 159 | "index": 0, 160 | "name": "Bing", 161 | "keyword": "b", 162 | "keyboardShortcut": "", 163 | "multitab": false, 164 | "url": "https://www.bing.com/search?q=", 165 | "show": true, 166 | "base64": "" 167 | }, 168 | "google": { 169 | "index": 1, 170 | "name": "Google", 171 | "keyword": "g", 172 | "keyboardShortcut": "", 173 | "multitab": false, 174 | "url": "https://www.google.com/search?q=", 175 | "show": true, 176 | "base64": "" 177 | }, 178 | "yahoo": { 179 | "index": 2, 180 | "name": "Yahoo!", 181 | "keyword": "y", 182 | "keyboardShortcut": "", 183 | "multitab": false, 184 | "url": "https://search.yahoo.com/search?p=", 185 | "show": true, 186 | "base64": "" 187 | } 188 | } 189 | ``` 190 | 191 | It is not required to provide the base 64 string representation of any search engine's favicon. This string will automatically be loaded for you. 192 | 193 | ## Special thanks to the following contributors 194 | 195 | 203 | 204 | Other translations were completed using [DeepL](https://www.deepl.com/translator), Microsoft Translator and Google Translate in [Crowdin](https://crowdin.com). 205 | 206 | ## Code made by others used in this extension 207 | 208 | - Webextension-polyfill v0.12.0 minified with many contributors, which can be found on GitHub here: https://github.com/mozilla/webextension-polyfill 209 | - SortableJS v1.15.6 minified with many contributors, which can be found on GitHub here: https://github.com/SortableJS/Sortable/blob/1.15.6/Sortable.min.js 210 | -------------------------------------------------------------------------------- /_locales/ar/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "البحث في السياق", 4 | "description": "Name of the extension" 5 | }, 6 | "extensionDescription": { 7 | "message": "ابحث عن النص المحدد باستخدام محرك بحث أو مطالبة الذكاء الاصطناعي من قائمة محركات البحث المخصصة ومطالبات الذكاء الاصطناعي التي تديرها في صفحة التفضيلات الخاصة بالملحق.", 8 | "description": "Description of the extension" 9 | }, 10 | "multisearch": { 11 | "message": " هو بحث بمحركات بحث متعددة في وقت واحد.", 12 | "description": "multi-search - it's a search with multiple search engines simultaneously." 13 | }, 14 | "multisearchHowTo": { 15 | "message": "حدد خانة الاختيار الوسطى لتضمين محرك بحث في" 16 | }, 17 | "multisearchLaunch": { 18 | "message": "يمكنك إطلاق" 19 | }, 20 | "multisearchEnd": { 21 | "message": "من قائمة السياق أو من شريط العناوين عن طريق الكتابة:" 22 | }, 23 | "multisearchEndOfSentence": { 24 | "message": "متبوعة بمصطلحات البحث الخاصة بك." 25 | }, 26 | "pageTitle": { 27 | "message": "تفضيلات البحث في السياق" 28 | }, 29 | "header1": { 30 | "message": "أين يجب أن تظهر نتائج البحث؟" 31 | }, 32 | "newTab": { 33 | "message": "في علامة تبويب جديدة" 34 | }, 35 | "sameTab": { 36 | "message": "في علامة التبويب نفسها" 37 | }, 38 | "newWindow": { 39 | "message": "في نافذة جديدة" 40 | }, 41 | "active": { 42 | "message": "جعل علامة التبويب أو النافذة الجديدة نشطة" 43 | }, 44 | "position": { 45 | "message": "عرض نتائج البحث بعد علامة التبويب الأخيرة" 46 | }, 47 | "header3": { 48 | "message": "موضع الخيارات في قائمة السياق" 49 | }, 50 | "subheader31": { 51 | "message": "أين تريد أن تظهر قائمة الخيارات في قائمة السياق؟" 52 | }, 53 | "bottom": { 54 | "message": "في الأسفل" 55 | }, 56 | "top": { 57 | "message": "في الأعلى" 58 | }, 59 | "hide": { 60 | "message": "لا هذا ولا ذاك (إخفاء قائمة الخيارات)" 61 | }, 62 | "header5": { 63 | "message": "محركات بحث الاستيراد/التصدير" 64 | }, 65 | "import": { 66 | "message": "استيراد JSON من القرص المحلي" 67 | }, 68 | "export": { 69 | "message": "تصدير محركات البحث إلى JSON" 70 | }, 71 | "download": { 72 | "message": "تصدير إلى القرص المحلي" 73 | }, 74 | "header2": { 75 | "message": "الأيقونات والرموز المفضلة" 76 | }, 77 | "note": { 78 | "message": "تتطلب هذه الميزة فايرفوكس 56 أو أعلى." 79 | }, 80 | "header4": { 81 | "message": "خيارات البحث" 82 | }, 83 | "exactMatch": { 84 | "message": "البحث عن سلسلة مطابقة تامة" 85 | }, 86 | "gridMode": { 87 | "message": "عرض شبكة من الرموز بدلاً من قائمة محركات البحث" 88 | }, 89 | "warning": { 90 | "message": "تحذير: يرجى ملاحظة أن تمكين هذه الميزة سيمنع عرض قوائم السياق من ملحقات أخرى." 91 | }, 92 | "savePreferences": { 93 | "message": "حفظ التفضيلات" 94 | }, 95 | "header6": { 96 | "message": "ما هي محركات البحث التي ترغب في ظهورها في قائمة السياق أو الشبكة؟" 97 | }, 98 | "clearAll": { 99 | "message": "مسح الكل" 100 | }, 101 | "selectAll": { 102 | "message": "اختر الكل" 103 | }, 104 | "sortAlphabetically": { 105 | "message": "فرز أبجدياً" 106 | }, 107 | "showAdvancedFeatures": { 108 | "message": "إظهار الميزات المتقدمة" 109 | }, 110 | "hideAdvancedFeatures": { 111 | "message": "إخفاء الميزات المتقدمة" 112 | }, 113 | "reset": { 114 | "message": "إعادة تعيين" 115 | }, 116 | "subheader61": { 117 | "message": "إضافة محرك بحث جديد" 118 | }, 119 | "showSearchEngine": { 120 | "message": "إظهار محرك البحث في الشبكة أو قائمة السياق" 121 | }, 122 | "searchEngineName": { 123 | "message": "اسم محرك البحث" 124 | }, 125 | "placeHolderKeyword": { 126 | "message": "الكلمة الرئيسية" 127 | }, 128 | "warningText": { 129 | "message": "تحذير" 130 | }, 131 | "keyboardShortcutWarning": { 132 | "message": "لن تعمل جميع مجموعات المفاتيح. قد لا تبدأ اختصارات لوحة المفاتيح ب CMD على ماك أو CTRL على ويندوز أو لينكس، لأن هذه الاختصارات عادةً ما تكون محجوزة لأوامر فايرفوكس." 133 | }, 134 | "placeHolderKeyboardShortcut": { 135 | "message": "اختصار لوحة المفاتيح" 136 | }, 137 | "multipleSearchEngines": { 138 | "message": "التضمين في البحث المتعدد" 139 | }, 140 | "searchEngineURL": { 141 | "message": "عنوان URL لمحرك البحث" 142 | }, 143 | "regex": { 144 | "message": "تعبير عادي" 145 | }, 146 | "testSearchEngine": { 147 | "message": "اختبار محرك البحث" 148 | }, 149 | "addSearchEngine": { 150 | "message": "إضافة محرك بحث" 151 | }, 152 | "addSeparator": { 153 | "message": "إضافة فاصل" 154 | }, 155 | "clear": { 156 | "message": "واضح" 157 | }, 158 | "disableGrid": { 159 | "message": "تعطيل شبكة الأيقونات" 160 | }, 161 | "move": { 162 | "message": "الانتقال" 163 | }, 164 | "up": { 165 | "message": "لأعلى" 166 | }, 167 | "down": { 168 | "message": "لأسفل" 169 | }, 170 | "remove": { 171 | "message": "إزالة" 172 | }, 173 | "multipleSearchEnginesSearch": { 174 | "message": "قم بتضمين محرك البحث هذا في محرك بحث متعدد" 175 | }, 176 | "notifySavedPreferences": { 177 | "message": "التفضيلات المحفوظة." 178 | }, 179 | "notifySearchEngineAdded": { 180 | "message": "تمت إضافة محرك البحث." 181 | }, 182 | "titleShowEngine": { 183 | "message": "إظهار محرك البحث هذا في قائمة السياق أو الشبكة" 184 | }, 185 | "notifyEnableStorageSync": { 186 | "message": "يُرجى التأكد من ضبط العلامة \"webextensions.storage.sync.enabled\" على \"صواب\" في \"about:config\"، وإلا فلن يعمل بحث السياق." 187 | }, 188 | "notifySearchEnginesLoaded": { 189 | "message": "تم تحميل القائمة الافتراضية لمحركات البحث." 190 | }, 191 | "notifySearchEngineNotFound": { 192 | "message": "تعذر العثور على محرك بحث على صفحة الويب الحالية" 193 | }, 194 | "titleMultipleSearchEngines": { 195 | "message": "محركات البحث المتعددة البحث في محركات البحث" 196 | }, 197 | "titleAISearch": { 198 | "message": "بحث بالذكاء الاصطناعي" 199 | }, 200 | "titleSiteSearch": { 201 | "message": "ابحث في هذا الموقع باستخدام" 202 | }, 203 | "titleOptions": { 204 | "message": "الخيارات" 205 | }, 206 | "windowTitle": { 207 | "message": "نتائج البحث عن" 208 | }, 209 | "omniboxDescription": { 210 | "message": "البحث باستخدام بحث السياق مع الكلمات المفتاحية. الاستعمال: cs [كلمة مفتاحية] [مصطلحات البحث] (على سبيل المثال \"cs w Linux\" يبحث في ويكيبيديا عن مصطلح \"لينكس\")" 211 | }, 212 | "notifyUsage": { 213 | "message": "الاستخدام: cs [كلمة رئيسية] [مصطلحات البحث] (على سبيل المثال: cs w Linux)" 214 | }, 215 | "notifySearchEngineWithKeyword": { 216 | "message": "محرك البحث بالكلمة المفتاحية" 217 | }, 218 | "notifyUnknown": { 219 | "message": "غير معروفة." 220 | }, 221 | "notifySearchEngineUrlRequired": { 222 | "message": "يُرجى ملء عنوان URL صالح لمحرك البحث." 223 | }, 224 | "header7": { 225 | "message": "خيارات إعادة التعيين" 226 | }, 227 | "sidebar": { 228 | "message": "في الشريط الجانبي" 229 | }, 230 | "displayFavicons": { 231 | "message": "عرض المفضلة في قائمة السياق" 232 | }, 233 | "disableAltClick": { 234 | "message": "تعطيل شبكة الأيقونات (Alt + النقر)" 235 | }, 236 | "resetPreferences": { 237 | "message": "إعادة تعيين التفضيلات إلى الإعدادات الافتراضية" 238 | }, 239 | "forceSearchEnginesReload": { 240 | "message": "فرض إعادة تحميل قائمة محركات البحث الافتراضية" 241 | }, 242 | "forceFaviconsReload": { 243 | "message": "فرض إعادة تحميل الأيقونات المفضلة" 244 | }, 245 | "restriction": { 246 | "message": "إذا لم يكن الشريط الجانبي مفتوحًا بالفعل، فقد يتعين عليك النقر على أيقونة \"إظهار الأشرطة الجانبية\" من شريط أدوات المتصفح." 247 | }, 248 | "reverseImage": { 249 | "message": "أيضًا، لا يمكن عرض نتائج البحث العكسي عن الصور في الشريط الجانبي." 250 | }, 251 | "siteSearch": { 252 | "message": "محرك بحث للبحث في الموقع الحالي:" 253 | }, 254 | "header9": { 255 | "message": "الميزات المتقدمة" 256 | }, 257 | "useRegex": { 258 | "message": "استخدام التعبيرات العادية لإنشاء قائمة السياق" 259 | }, 260 | "regexNote": { 261 | "message": "لا تنطبق هذه الميزة على شبكة الأيقونات." 262 | }, 263 | "subheader62": { 264 | "message": "إضافة مجلد جديد" 265 | }, 266 | "folderName": { 267 | "message": "اسم المجلد" 268 | }, 269 | "folder": { 270 | "message": "المجلد" 271 | }, 272 | "addFolder": { 273 | "message": "إضافة مجلد" 274 | }, 275 | "keyboardShortcut": { 276 | "message": "اختصار لوحة المفاتيح" 277 | }, 278 | "header10": { 279 | "message": "خيارات البحث المتعدد" 280 | }, 281 | "multisearchIntro": { 282 | "message": "فتح نتائج/علامات تبويب بحث متعددة" 283 | }, 284 | "multiNewWindow": { 285 | "message": "في نافذة جديدة" 286 | }, 287 | "multiAfterLastTab": { 288 | "message": "بعد علامة التبويب الأخيرة في النافذة الحالية" 289 | }, 290 | "multiActiveTab": { 291 | "message": "مباشرة بعد علامة التبويب النشطة في النافذة الحالية" 292 | }, 293 | "privacy": { 294 | "message": "جعل النافذة الجديدة خاصة" 295 | }, 296 | "reminder": { 297 | "message": "تذكير" 298 | }, 299 | "enableDownloadsReminder": { 300 | "message": "تمكين أذونات التنزيلات لتصدير محركات البحث الخاصة بك." 301 | }, 302 | "header11": { 303 | "message": "خيارات شبكة الأيقونات" 304 | }, 305 | "quickIconGrid": { 306 | "message": "عرض شبكة الرموز مباشرةً بعد تحديد النص" 307 | }, 308 | "closeGridOnMouseOut": { 309 | "message": "إغلاق شبكة الرموز عند خروج الفأرة" 310 | }, 311 | "xOffset": { 312 | "message": "إزاحة أفقية لشبكة الأيقونات: " 313 | }, 314 | "yOffset": { 315 | "message": "الإزاحة الرأسية لشبكة الأيقونات: " 316 | }, 317 | "clearKeyboardShortcuts": { 318 | "message": "مسح جميع اختصارات لوحة المفاتيح" 319 | }, 320 | "subheader63": { 321 | "message": "إضافة موجه ذكاء اصطناعي جديد" 322 | }, 323 | "subheader64": { 324 | "message": "إضافة فاصل" 325 | }, 326 | "testChatGPT": { 327 | "message": "اختبار موجه الذكاء الاصطناعي" 328 | }, 329 | "addChatGPTPrompt": { 330 | "message": "إضافة موجه الذكاء الاصطناعي" 331 | }, 332 | "promptShow": { 333 | "message": "إظهار المطالبة في الشبكة أو قائمة السياق" 334 | }, 335 | "promptName": { 336 | "message": "اسم الموجه" 337 | }, 338 | "prompt": { 339 | "message": "موجه" 340 | }, 341 | "overwriteSearchEngines": { 342 | "message": "الكتابة فوق محركات البحث الحالية بمحركات البحث المستوردة" 343 | }, 344 | "removeFolderConfirmPrefix": { 345 | "message": "هل أنت متأكد من رغبتك في إزالة المجلد" 346 | }, 347 | "removeFolderConfirmSuffix": { 348 | "message": "وجميع محتوياته" 349 | }, 350 | "removeSearchEngineConfirm": { 351 | "message": "هل أنت متأكد من رغبتك في إزالة محرك البحث" 352 | }, 353 | "popupBlockedMessage": { 354 | "message": "تم حظر النوافذ المنبثقة! يرجى السماح بالنوافذ المنبثقة لهذا الموقع." 355 | }, 356 | "subscriptionOptions": { 357 | "message": "خيارات الاشتراك في البحث في السياق" 358 | }, 359 | "startNewTrial": { 360 | "message": "ابدأ تجربة جديدة لمدة 7 أيام" 361 | }, 362 | "startSubscription": { 363 | "message": "ابدأ اشتراكًا جديدًا شهريًا أو سنويًا أو مدى الحياة" 364 | }, 365 | "subscriptionStatus": { 366 | "message": "حالة الاشتراك" 367 | }, 368 | "notifyMissingSearchEngine": { 369 | "message": "لم يتم اختيار أي محركات بحث متعددة البحث" 370 | }, 371 | "notifyMissingBookmarkUrl": { 372 | "message": "تعذر استرداد عنوان URL لإزالة الإشارة المرجعية" 373 | }, 374 | "notifyAIMinimalRequirements": { 375 | "message": "يرجى تحديد موفر ذكاء اصطناعي على الأقل وتقديم اسم موجه وموجه" 376 | }, 377 | "bookmarkPage": { 378 | "message": "ضع إشارة مرجعية على هذه الصفحة" 379 | }, 380 | "unbookmarkPage": { 381 | "message": "إلغاء وضع إشارة مرجعية على هذه الصفحة" 382 | }, 383 | "pay": { 384 | "message": "الراتب" 385 | }, 386 | "startTrial": { 387 | "message": "بدء التجربة" 388 | }, 389 | "trialActive": { 390 | "message": "التجربة نشطة: " 391 | }, 392 | "trialExpired": { 393 | "message": "انتهت صلاحية التجربة" 394 | }, 395 | "subscriptionActive": { 396 | "message": "الاشتراك: نشط" 397 | }, 398 | "subscriptionInactive": { 399 | "message": "اشتراك غير نشط" 400 | }, 401 | "noTrialStarted": { 402 | "message": "لم تبدأ أي محاكمة" 403 | }, 404 | "daysRemaining": { 405 | "message": "اليوم (الأيام) المتبقية" 406 | }, 407 | "__WET_LOCALE__": { 408 | "message": "ar" 409 | } 410 | } -------------------------------------------------------------------------------- /_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Context Search", 4 | "description": "Name of the extension" 5 | }, 6 | "extensionDescription": { 7 | "message": "Searches the selected text using a search engine or AI prompt from your custom list of search engines and AI prompts managed in the extension's preferences page.", 8 | "description": "Description of the extension" 9 | }, 10 | "multisearch": { 11 | "message": " is a search with multiple search engines simultaneously.", 12 | "description": "multi-search - it's a search with multiple search engines simultaneously." 13 | }, 14 | "multisearchHowTo": { 15 | "message": "Check the middle checkbox to include a search engine in a" 16 | }, 17 | "multisearchLaunch": { 18 | "message": "You can launch a" 19 | }, 20 | "multisearchEnd": { 21 | "message": "from the context menu or from the address bar by typing:" 22 | }, 23 | "multisearchEndOfSentence": { 24 | "message": "followed by your search terms." 25 | }, 26 | "pageTitle": { 27 | "message": "Context Search Preferences" 28 | }, 29 | "header1": { 30 | "message": "Where should search results appear?" 31 | }, 32 | "newTab": { 33 | "message": "In a new tab" 34 | }, 35 | "sameTab": { 36 | "message": "In the same tab" 37 | }, 38 | "newWindow": { 39 | "message": "In a new window" 40 | }, 41 | "active": { 42 | "message": "Make the new tab or window active" 43 | }, 44 | "position": { 45 | "message": "Display the search results after the last tab" 46 | }, 47 | "header3": { 48 | "message": "Position of options in the context menu" 49 | }, 50 | "subheader31": { 51 | "message": "Where would you like the options menu to appear in the context menu?" 52 | }, 53 | "bottom": { 54 | "message": "At the bottom" 55 | }, 56 | "top": { 57 | "message": "At the top" 58 | }, 59 | "hide": { 60 | "message": "Neither (hide the options menu)" 61 | }, 62 | "header5": { 63 | "message": "Import/export search engines" 64 | }, 65 | "import": { 66 | "message": "Import JSON from local disk" 67 | }, 68 | "export": { 69 | "message": "Export search engines to JSON" 70 | }, 71 | "download": { 72 | "message": "Export to local disk" 73 | }, 74 | "header2": { 75 | "message": "Favicons & Icons" 76 | }, 77 | "note": { 78 | "message": "This feature requires Firefox 56 or above." 79 | }, 80 | "header4": { 81 | "message": "Search options" 82 | }, 83 | "exactMatch": { 84 | "message": "Search for an exact string match" 85 | }, 86 | "gridMode": { 87 | "message": "Display a grid of icons instead of a list of search engines" 88 | }, 89 | "warning": { 90 | "message": "Warning: Please note that enabling this feature will prevent context menus from other extensions to be displayed." 91 | }, 92 | "savePreferences": { 93 | "message": "Save preferences" 94 | }, 95 | "header6": { 96 | "message": "Which search engines would you like to appear in the context menu or grid?" 97 | }, 98 | "clearAll": { 99 | "message": "Clear All" 100 | }, 101 | "selectAll": { 102 | "message": "Select All" 103 | }, 104 | "sortAlphabetically": { 105 | "message": "Sort alphabetically" 106 | }, 107 | "showAdvancedFeatures": { 108 | "message": "Show advanced features" 109 | }, 110 | "hideAdvancedFeatures": { 111 | "message": "Hide advanced features" 112 | }, 113 | "reset": { 114 | "message": "Reset" 115 | }, 116 | "subheader61": { 117 | "message": "Add a new search engine" 118 | }, 119 | "showSearchEngine": { 120 | "message": "Show search engine in grid or context menu" 121 | }, 122 | "searchEngineName": { 123 | "message": "Search engine name" 124 | }, 125 | "placeHolderKeyword": { 126 | "message": "Keyword" 127 | }, 128 | "warningText": { 129 | "message": "Warning" 130 | }, 131 | "keyboardShortcutWarning": { 132 | "message": "not all key combinations will work. Keyboard shortcuts may not start with CMD on macOS or CTRL on Windows or Linux, as these are usually reserved for Firefox commands." 133 | }, 134 | "placeHolderKeyboardShortcut": { 135 | "message": "Keyboard shortcut" 136 | }, 137 | "multipleSearchEngines": { 138 | "message": "Include in multi-search" 139 | }, 140 | "searchEngineURL": { 141 | "message": "Search engine URL" 142 | }, 143 | "regex": { 144 | "message": "Regular expression" 145 | }, 146 | "testSearchEngine": { 147 | "message": "Test search engine" 148 | }, 149 | "addSearchEngine": { 150 | "message": "Add search engine" 151 | }, 152 | "addSeparator": { 153 | "message": "Add a separator" 154 | }, 155 | "clear": { 156 | "message": "Clear" 157 | }, 158 | "disableGrid": { 159 | "message": "Disable grid of icons" 160 | }, 161 | "move": { 162 | "message": "Move" 163 | }, 164 | "up": { 165 | "message": "up" 166 | }, 167 | "down": { 168 | "message": "down" 169 | }, 170 | "remove": { 171 | "message": "Remove" 172 | }, 173 | "multipleSearchEnginesSearch": { 174 | "message": "Include this search engine in a multi-search" 175 | }, 176 | "notifySavedPreferences": { 177 | "message": "Saved preferences." 178 | }, 179 | "notifySearchEngineAdded": { 180 | "message": "Search engine added." 181 | }, 182 | "titleShowEngine": { 183 | "message": "Show this search engine in the context menu or grid" 184 | }, 185 | "notifyEnableStorageSync": { 186 | "message": "Please ensure that the flag 'webextensions.storage.sync.enabled' is set to 'true' in 'about:config', otherwise Context Search won't work." 187 | }, 188 | "notifySearchEnginesLoaded": { 189 | "message": "Default list of search engines has been loaded." 190 | }, 191 | "notifySearchEngineNotFound": { 192 | "message": "A search engine couldn't be found on the current web page" 193 | }, 194 | "titleMultipleSearchEngines": { 195 | "message": "Multiple search engines search" 196 | }, 197 | "titleAISearch": { 198 | "message": "AI Search" 199 | }, 200 | "titleSiteSearch": { 201 | "message": "Search this site with" 202 | }, 203 | "titleOptions": { 204 | "message": "Options" 205 | }, 206 | "windowTitle": { 207 | "message": "Search results for" 208 | }, 209 | "omniboxDescription": { 210 | "message": "Search using Context Search with keywords. Usage: cs [keyword] [search terms] (e.g. 'cs w Linux' searches Wikipedia for the term 'Linux')" 211 | }, 212 | "notifyUsage": { 213 | "message": "Usage: cs [keyword] [search terms] (for example: cs w Linux)" 214 | }, 215 | "notifySearchEngineWithKeyword": { 216 | "message": "Search engine with keyword" 217 | }, 218 | "notifyUnknown": { 219 | "message": "is unknown." 220 | }, 221 | "notifySearchEngineUrlRequired": { 222 | "message": "Please fill in a valid search engine URL." 223 | }, 224 | "header7": { 225 | "message": "Reset options" 226 | }, 227 | "sidebar": { 228 | "message": "In the sidebar" 229 | }, 230 | "displayFavicons": { 231 | "message": "Display favicons in the context menu" 232 | }, 233 | "disableAltClick": { 234 | "message": "Disable icon grid (Alt + Click)" 235 | }, 236 | "resetPreferences": { 237 | "message": "Reset preferences to default settings" 238 | }, 239 | "forceSearchEnginesReload": { 240 | "message": "Force reload of default search engines list" 241 | }, 242 | "forceFaviconsReload": { 243 | "message": "Force favicons to be reloaded" 244 | }, 245 | "restriction": { 246 | "message": "If the sidebar isn't already open, then you may have to click on the 'Show sidebars' icon from the browser toolbar." 247 | }, 248 | "reverseImage": { 249 | "message": "Also, reverse image search results cannot be displayed in the sidebar." 250 | }, 251 | "siteSearch": { 252 | "message": "Search engine for searching current site:" 253 | }, 254 | "header9": { 255 | "message": "Advanced features" 256 | }, 257 | "useRegex": { 258 | "message": "Use regular expressions to build the context menu" 259 | }, 260 | "regexNote": { 261 | "message": "This feature doesn't apply to the grid of icons." 262 | }, 263 | "subheader62": { 264 | "message": "Add a new folder" 265 | }, 266 | "folderName": { 267 | "message": "Folder name" 268 | }, 269 | "folder": { 270 | "message": "folder" 271 | }, 272 | "addFolder": { 273 | "message": "Add folder" 274 | }, 275 | "keyboardShortcut": { 276 | "message": "Keyboard shortcut" 277 | }, 278 | "header10": { 279 | "message": "Multisearch options" 280 | }, 281 | "multisearchIntro": { 282 | "message": "Open multiple search results/tabs" 283 | }, 284 | "multiNewWindow": { 285 | "message": "In a new window" 286 | }, 287 | "multiAfterLastTab": { 288 | "message": "After the last tab in the current window" 289 | }, 290 | "multiActiveTab": { 291 | "message": "Right after the active tab in the current window" 292 | }, 293 | "privacy": { 294 | "message": "Make new window private" 295 | }, 296 | "reminder": { 297 | "message": "Reminder" 298 | }, 299 | "enableDownloadsReminder": { 300 | "message": "enable Downloads permissions to export your search engines." 301 | }, 302 | "header11": { 303 | "message": "Icons Grid options" 304 | }, 305 | "quickIconGrid": { 306 | "message": "Immediately display the icons grid after text has been selected" 307 | }, 308 | "closeGridOnMouseOut": { 309 | "message": "Close the icons grid on mouse out" 310 | }, 311 | "xOffset": { 312 | "message": "Horizontal offset of the icons grid: " 313 | }, 314 | "yOffset": { 315 | "message": "Vertical offset of the icons grid: " 316 | }, 317 | "clearKeyboardShortcuts": { 318 | "message": "Clear all keyboard shortcuts" 319 | }, 320 | "subheader63": { 321 | "message": "Add a new AI prompt" 322 | }, 323 | "subheader64": { 324 | "message": "Add a separator" 325 | }, 326 | "testChatGPT": { 327 | "message": "Test AI Prompt" 328 | }, 329 | "addChatGPTPrompt": { 330 | "message": "Add AI Prompt" 331 | }, 332 | "promptShow": { 333 | "message": "Show prompt in grid or context menu" 334 | }, 335 | "promptName": { 336 | "message": "Prompt name" 337 | }, 338 | "prompt": { 339 | "message": "Prompt" 340 | }, 341 | "overwriteSearchEngines": { 342 | "message": "Overwrite the existing search engines with the imported search engines" 343 | }, 344 | "removeFolderConfirmPrefix": { 345 | "message": "Are you sure that you want to remove the folder" 346 | }, 347 | "removeFolderConfirmSuffix": { 348 | "message": "and all of its contents" 349 | }, 350 | "removeSearchEngineConfirm": { 351 | "message": "Are you sure you want to remove the search engine" 352 | }, 353 | "popupBlockedMessage": { 354 | "message": "Popup blocked! Please allow popups for this site." 355 | }, 356 | "subscriptionOptions": { 357 | "message": "Context Search Subscription Options" 358 | }, 359 | "startNewTrial": { 360 | "message": "Start a new 7 days trial" 361 | }, 362 | "startSubscription": { 363 | "message": "Start a new monthly, yearly or lifetime subscription" 364 | }, 365 | "subscriptionStatus": { 366 | "message": "Subscription status" 367 | }, 368 | "notifyMissingSearchEngine": { 369 | "message": "No search engines have been selected for a multi-search" 370 | }, 371 | "notifyMissingBookmarkUrl": { 372 | "message": "Could not retrieve URL for bookmark removal" 373 | }, 374 | "notifyAIMinimalRequirements": { 375 | "message": "Please at least select an AI Provider and provide a prompt name and a prompt" 376 | }, 377 | "bookmarkPage": { 378 | "message": "Bookmark This Page" 379 | }, 380 | "unbookmarkPage": { 381 | "message": "Unbookmark This Page" 382 | }, 383 | "pay": { 384 | "message": "Pay" 385 | }, 386 | "startTrial": { 387 | "message": "Start trial" 388 | }, 389 | "trialActive": { 390 | "message": "Trial active: " 391 | }, 392 | "trialExpired": { 393 | "message": "Trial expired" 394 | }, 395 | "subscriptionActive": { 396 | "message": "Subscription: Active" 397 | }, 398 | "subscriptionInactive": { 399 | "message": "Subscription: Inactive" 400 | }, 401 | "noTrialStarted": { 402 | "message": "No trial started" 403 | }, 404 | "daysRemaining": { 405 | "message": "day(s) remaining" 406 | }, 407 | "__WET_LOCALE__": { 408 | "message": "en" 409 | } 410 | } -------------------------------------------------------------------------------- /_locales/fa/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Context Search", 4 | "description": "Name of the extension" 5 | }, 6 | "extensionDescription": { 7 | "message": "متن انتخاب شده را با استفاده از یک موتور جستجو یا اعلان هوش مصنوعی از لیست سفارشی موتورهای جستجو و درخواست های هوش مصنوعی مدیریت شده در صفحه تنظیمات برگزیده برنامه افزودنی جستجو می کند.", 8 | "description": "Description of the extension" 9 | }, 10 | "multisearch": { 11 | "message": " جستجو با چندین موتور جستجو به طور همزمان است.", 12 | "description": "multi-search - it's a search with multiple search engines simultaneously." 13 | }, 14 | "multisearchHowTo": { 15 | "message": "چک باکس وسط را علامت بزنید تا یک موتور جستجو در یک" 16 | }, 17 | "multisearchLaunch": { 18 | "message": "می توانید یک" 19 | }, 20 | "multisearchEnd": { 21 | "message": "از منوی زمینه یا از نوار آدرس با تایپ کردن:" 22 | }, 23 | "multisearchEndOfSentence": { 24 | "message": "به دنبال آن عبارات جستجوی شما قرار می گیرد." 25 | }, 26 | "pageTitle": { 27 | "message": "تنظیمات برگزیده جستجوی زمینه" 28 | }, 29 | "header1": { 30 | "message": "نتایج جستجو کجا باید نشان داده شود؟" 31 | }, 32 | "newTab": { 33 | "message": "در یک برگه جدید" 34 | }, 35 | "sameTab": { 36 | "message": "در همان برگه" 37 | }, 38 | "newWindow": { 39 | "message": "در یک پنجره جدید" 40 | }, 41 | "active": { 42 | "message": "فعال کردن برگه یا پنجره جدید" 43 | }, 44 | "position": { 45 | "message": "نمایش نتایج جستجو بعد از آخرین برگه" 46 | }, 47 | "header3": { 48 | "message": "موقعیت گزینه ها در منوی زمینه" 49 | }, 50 | "subheader31": { 51 | "message": "دوست دارید منوی گزینه ها در کجای منوی زمینه ظاهر شود؟" 52 | }, 53 | "bottom": { 54 | "message": "در پایین" 55 | }, 56 | "top": { 57 | "message": "در بالا" 58 | }, 59 | "hide": { 60 | "message": "هیچ کدام (منوی گزینه ها را مخفی کنید)" 61 | }, 62 | "header5": { 63 | "message": "واردات/صادرات موتورهای جستجو" 64 | }, 65 | "import": { 66 | "message": "JSON را از دیسک محلی وارد کنید" 67 | }, 68 | "export": { 69 | "message": "موتورهای جستجو را به JSON صادر کنید" 70 | }, 71 | "download": { 72 | "message": "صادرات به دیسک محلی" 73 | }, 74 | "header2": { 75 | "message": "فاویکون ها و آیکون ها" 76 | }, 77 | "note": { 78 | "message": "این ویژگی به فایرفاکس 56 یا بالاتر نیاز دارد." 79 | }, 80 | "header4": { 81 | "message": "گزینه های جستجو" 82 | }, 83 | "exactMatch": { 84 | "message": "جستجوی یک تطابق دقیق رشته" 85 | }, 86 | "gridMode": { 87 | "message": "نمایش شبکه ای از نمادها به جای لیستی از موتورهای جستجو" 88 | }, 89 | "warning": { 90 | "message": "هشدار: لطفا توجه داشته باشید که فعال کردن این ویژگی از نمایش منوهای زمینه سایر افزونه ها جلوگیری می کند." 91 | }, 92 | "savePreferences": { 93 | "message": "ذخیره تنظیمات برگزیده" 94 | }, 95 | "header6": { 96 | "message": "کدام موتورهای جستجو را می خواهید در منوی زمینه یا شبکه ظاهر شوند؟" 97 | }, 98 | "clearAll": { 99 | "message": "پاک کردن همه" 100 | }, 101 | "selectAll": { 102 | "message": "همه را انتخاب کنید" 103 | }, 104 | "sortAlphabetically": { 105 | "message": "مرتب سازی بر اساس حروف الفبا" 106 | }, 107 | "showAdvancedFeatures": { 108 | "message": "نمایش ویژگی های پیشرفته" 109 | }, 110 | "hideAdvancedFeatures": { 111 | "message": "مخفی کردن ویژگی های پیشرفته" 112 | }, 113 | "reset": { 114 | "message": "تنظیم مجدد" 115 | }, 116 | "subheader61": { 117 | "message": "افزودن موتور جستجوی جدید" 118 | }, 119 | "showSearchEngine": { 120 | "message": "نمایش موتور جستجو در منوی شبکه یا زمینه" 121 | }, 122 | "searchEngineName": { 123 | "message": "نام موتور جستجو" 124 | }, 125 | "placeHolderKeyword": { 126 | "message": "کلمه کلیدی" 127 | }, 128 | "warningText": { 129 | "message": "هشدار" 130 | }, 131 | "keyboardShortcutWarning": { 132 | "message": "همه ترکیب های کلیدی کار نمی کنند. میانبرهای صفحه کلید ممکن است با CMD در macOS یا CTRL در ویندوز یا لینوکس شروع نشوند، زیرا این موارد معمولا برای دستورات فایرفاکس محفوظ هستند." 133 | }, 134 | "placeHolderKeyboardShortcut": { 135 | "message": "میانبر صفحه کلید" 136 | }, 137 | "multipleSearchEngines": { 138 | "message": "در جستجوی چندگانه گنجانده شود" 139 | }, 140 | "searchEngineURL": { 141 | "message": "URL موتور جستجو" 142 | }, 143 | "regex": { 144 | "message": "بیان منظم" 145 | }, 146 | "testSearchEngine": { 147 | "message": "تست موتور جستجو" 148 | }, 149 | "addSearchEngine": { 150 | "message": "افزودن موتور جستجو" 151 | }, 152 | "addSeparator": { 153 | "message": "افزودن یک جداکننده" 154 | }, 155 | "clear": { 156 | "message": "روشن" 157 | }, 158 | "disableGrid": { 159 | "message": "شبکه آیکون ها را غیرفعال کنید" 160 | }, 161 | "move": { 162 | "message": "حرکت" 163 | }, 164 | "up": { 165 | "message": "تا" 166 | }, 167 | "down": { 168 | "message": "پایین" 169 | }, 170 | "remove": { 171 | "message": "حذف" 172 | }, 173 | "multipleSearchEnginesSearch": { 174 | "message": "این موتور جستجو را در یک جستجوی چندگانه قرار دهید" 175 | }, 176 | "notifySavedPreferences": { 177 | "message": "تنظیمات برگزیده ذخیره شده ضربه بزنید؛" 178 | }, 179 | "notifySearchEngineAdded": { 180 | "message": "موتور جستجو اضافه شد." 181 | }, 182 | "titleShowEngine": { 183 | "message": "این موتور جستجو را در منوی زمینه یا شبکه نمایش دهید" 184 | }, 185 | "notifyEnableStorageSync": { 186 | "message": "لطفا مطمئن شوید که پرچم 'webextensions.storage.sync.enabled' روی 'true' در 'about:config' تنظیم شده است، در غیر این صورت جستجوی زمینه کار نخواهد کرد." 187 | }, 188 | "notifySearchEnginesLoaded": { 189 | "message": "لیست پیش فرض موتورهای جستجو بارگذاری شده است." 190 | }, 191 | "notifySearchEngineNotFound": { 192 | "message": "موتور جستجو در صفحه وب فعلی یافت نمی شود" 193 | }, 194 | "titleMultipleSearchEngines": { 195 | "message": "جستجوی چندین موتورهای جستجو" 196 | }, 197 | "titleAISearch": { 198 | "message": "جستجوی هوش مصنوعی" 199 | }, 200 | "titleSiteSearch": { 201 | "message": "این سایت را با" 202 | }, 203 | "titleOptions": { 204 | "message": "گزینه" 205 | }, 206 | "windowTitle": { 207 | "message": "نتایج جستجو برای" 208 | }, 209 | "omniboxDescription": { 210 | "message": "با استفاده از جستجوی زمینه با کلمات کلیدی جستجو کنید. طریقه استفاده: cs [کلمه کلیدی] [عبارت های جستجو] (به عنوان مثال 'cs w Linux' در ویکی پدیا برای اصطلاح 'لینوکس' جستجو می کند)" 211 | }, 212 | "notifyUsage": { 213 | "message": "طریقه استفاده: cs [کلمه کلیدی] [عبارت های جستجو] (به عنوان مثال: cs w لینوکس)" 214 | }, 215 | "notifySearchEngineWithKeyword": { 216 | "message": "موتور جستجو با کلمه کلیدی" 217 | }, 218 | "notifyUnknown": { 219 | "message": "ناشناخته است." 220 | }, 221 | "notifySearchEngineUrlRequired": { 222 | "message": "لطفا یک URL معتبر موتور جستجو را پر کنید." 223 | }, 224 | "header7": { 225 | "message": "گزینه های بازنشانی" 226 | }, 227 | "sidebar": { 228 | "message": "در نوار کناری" 229 | }, 230 | "displayFavicons": { 231 | "message": "نمایش فاویکون ها در منوی زمینه" 232 | }, 233 | "disableAltClick": { 234 | "message": "غیرفعال کردن شبکه آیکون (Alt + Click)" 235 | }, 236 | "resetPreferences": { 237 | "message": "بازنشانی تنظیمات برگزیده به تنظیمات پیش فرض" 238 | }, 239 | "forceSearchEnginesReload": { 240 | "message": "بارگذاری مجدد لیست موتورهای جستجوی پیش فرض" 241 | }, 242 | "forceFaviconsReload": { 243 | "message": "فاویکون ها را مجبور به بارگیری مجدد کنید" 244 | }, 245 | "restriction": { 246 | "message": "اگر نوار کناری از قبل باز نیست، ممکن است مجبور شوید روی نماد «نمایش نوارهای کناری» از نوار ابزار مرورگر کلیک کنید." 247 | }, 248 | "reverseImage": { 249 | "message": "همچنین، نتایج جستجوی تصویر معکوس را نمی توان در نوار کناری نمایش داد." 250 | }, 251 | "siteSearch": { 252 | "message": "موتور جستجو برای جستجوی سایت فعلی:" 253 | }, 254 | "header9": { 255 | "message": "ویژگی های پیشرفته" 256 | }, 257 | "useRegex": { 258 | "message": "استفاده از عبارات منظم برای ساخت منوی زمینه" 259 | }, 260 | "regexNote": { 261 | "message": "این ویژگی برای شبکه آیکون ها اعمال نمی شود." 262 | }, 263 | "subheader62": { 264 | "message": "یک پوشه جدید اضافه کنید" 265 | }, 266 | "folderName": { 267 | "message": "نام پوشه" 268 | }, 269 | "folder": { 270 | "message": "پوشه" 271 | }, 272 | "addFolder": { 273 | "message": "پوشه را اضافه کنید" 274 | }, 275 | "keyboardShortcut": { 276 | "message": "میانبر صفحه کلید" 277 | }, 278 | "header10": { 279 | "message": "گزینه های چند جستجو" 280 | }, 281 | "multisearchIntro": { 282 | "message": "باز کردن چندین نتایج/برگه جستجو" 283 | }, 284 | "multiNewWindow": { 285 | "message": "در یک پنجره جدید" 286 | }, 287 | "multiAfterLastTab": { 288 | "message": "بعد از آخرین برگه در پنجره فعلی" 289 | }, 290 | "multiActiveTab": { 291 | "message": "درست بعد از برگه فعال در پنجره فعلی" 292 | }, 293 | "privacy": { 294 | "message": "خصوصی کردن پنجره جدید" 295 | }, 296 | "reminder": { 297 | "message": "یادآوری" 298 | }, 299 | "enableDownloadsReminder": { 300 | "message": "مجوزهای دانلودها را برای صادرات موتورهای جستجوی خود فعال کنید." 301 | }, 302 | "header11": { 303 | "message": "گزینه های Icons Grid" 304 | }, 305 | "quickIconGrid": { 306 | "message": "بلافاصله پس از انتخاب متن، شبکه آیکون ها را نمایش دهید" 307 | }, 308 | "closeGridOnMouseOut": { 309 | "message": "شبکه آیکون ها را روی ماوس ببندید" 310 | }, 311 | "xOffset": { 312 | "message": "افست افقی شبکه آیکون ها: " 313 | }, 314 | "yOffset": { 315 | "message": "افست عمودی شبکه آیکون ها: " 316 | }, 317 | "clearKeyboardShortcuts": { 318 | "message": "پاک کردن همه میان برهای صفحه کلید" 319 | }, 320 | "subheader63": { 321 | "message": "یک اعلان هوش مصنوعی جدید اضافه کنید" 322 | }, 323 | "subheader64": { 324 | "message": "افزودن یک جداکننده" 325 | }, 326 | "testChatGPT": { 327 | "message": "تست AI Prompt" 328 | }, 329 | "addChatGPTPrompt": { 330 | "message": "اضافه کردن AI Prompt" 331 | }, 332 | "promptShow": { 333 | "message": "نمایش اعلان در منوی شبکه یا زمینه" 334 | }, 335 | "promptName": { 336 | "message": "نام اعلان" 337 | }, 338 | "prompt": { 339 | "message": "اعلان" 340 | }, 341 | "overwriteSearchEngines": { 342 | "message": "بازنویسی موتورهای جستجوی موجود با موتورهای جستجوی وارد شده" 343 | }, 344 | "removeFolderConfirmPrefix": { 345 | "message": "آیا مطمئن هستید که می خواهید پوشه را حذف کنید" 346 | }, 347 | "removeFolderConfirmSuffix": { 348 | "message": "و تمام محتویات آن" 349 | }, 350 | "removeSearchEngineConfirm": { 351 | "message": "آیا مطمئن هستید که می خواهید موتور جستجو را حذف کنید" 352 | }, 353 | "popupBlockedMessage": { 354 | "message": "پنجره بازشو مسدود شد! لطفا پنجره های بازشو را برای این سایت مجاز کنید." 355 | }, 356 | "subscriptionOptions": { 357 | "message": "گزینه های اشتراک جستجوی زمینه" 358 | }, 359 | "startNewTrial": { 360 | "message": "یک دوره آزمایشی 7 روزه جدید را شروع کنید" 361 | }, 362 | "startSubscription": { 363 | "message": "اشتراک ماهانه، سالانه یا مادام العمر جدید را شروع کنید" 364 | }, 365 | "subscriptionStatus": { 366 | "message": "وضعیت اشتراک" 367 | }, 368 | "notifyMissingSearchEngine": { 369 | "message": "هیچ موتور جستجویی برای جستجوی چندگانه انتخاب نشده است" 370 | }, 371 | "notifyMissingBookmarkUrl": { 372 | "message": "نمی توان URL را برای حذف نشانک بازیابی کرد" 373 | }, 374 | "notifyAIMinimalRequirements": { 375 | "message": "لطفا حداقل یک ارائه دهنده هوش مصنوعی را انتخاب کنید و یک نام سریع و یک اعلان ارائه دهید" 376 | }, 377 | "bookmarkPage": { 378 | "message": "این صفحه را نشانه گذاری کنید" 379 | }, 380 | "unbookmarkPage": { 381 | "message": "این صفحه را از نشانک بردارید" 382 | }, 383 | "pay": { 384 | "message": "پرداخت" 385 | }, 386 | "startTrial": { 387 | "message": "شروع آزمایشی" 388 | }, 389 | "trialActive": { 390 | "message": "کارآزمایی فعال: " 391 | }, 392 | "trialExpired": { 393 | "message": "دوره آزمایشی منقضی شده است" 394 | }, 395 | "subscriptionActive": { 396 | "message": "اشتراک: فعال" 397 | }, 398 | "subscriptionInactive": { 399 | "message": "اشتراک: غیر فعال" 400 | }, 401 | "noTrialStarted": { 402 | "message": "هیچ کارآزمایی ای شروع نشد" 403 | }, 404 | "daysRemaining": { 405 | "message": "روز(های) باقی مانده" 406 | }, 407 | "__WET_LOCALE__": { 408 | "message": "fa" 409 | } 410 | } -------------------------------------------------------------------------------- /_locales/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "コンテキスト検索", 4 | "description": "Name of the extension" 5 | }, 6 | "extensionDescription": { 7 | "message": "拡張機能の設定ページで管理されている検索エンジンとAIプロンプトのカスタムリストから、検索エンジンまたはAIプロンプトを使用して選択したテキストを検索します。", 8 | "description": "Description of the extension" 9 | }, 10 | "multisearch": { 11 | "message": " は複数の検索エンジンを同時に使った検索である。", 12 | "description": "multi-search - it's a search with multiple search engines simultaneously." 13 | }, 14 | "multisearchHowTo": { 15 | "message": "真ん中のチェックボックスをチェックして、検索エンジンを" 16 | }, 17 | "multisearchLaunch": { 18 | "message": "を起動することができます。" 19 | }, 20 | "multisearchEnd": { 21 | "message": "をコンテキストメニューから、またはアドレスバーから入力する:" 22 | }, 23 | "multisearchEndOfSentence": { 24 | "message": "の後に検索語を続ける。" 25 | }, 26 | "pageTitle": { 27 | "message": "コンテキスト検索の設定" 28 | }, 29 | "header1": { 30 | "message": "検索結果はどこに表示されるべきか?" 31 | }, 32 | "newTab": { 33 | "message": "新しいタブで" 34 | }, 35 | "sameTab": { 36 | "message": "同じタブで" 37 | }, 38 | "newWindow": { 39 | "message": "新しいウィンドウで表示" 40 | }, 41 | "active": { 42 | "message": "新しいタブまたはウィンドウをアクティブにする" 43 | }, 44 | "position": { 45 | "message": "最後のタブの後に検索結果を表示する" 46 | }, 47 | "header3": { 48 | "message": "コンテキストメニューのオプションの位置" 49 | }, 50 | "subheader31": { 51 | "message": "コンテキストメニューのどこにオプションメニューを表示しますか?" 52 | }, 53 | "bottom": { 54 | "message": "一番下" 55 | }, 56 | "top": { 57 | "message": "トップ" 58 | }, 59 | "hide": { 60 | "message": "どちらでもない(オプションメニューを隠す)" 61 | }, 62 | "header5": { 63 | "message": "検索エンジンのインポート/エクスポート" 64 | }, 65 | "import": { 66 | "message": "ローカルディスクからJSONをインポートする" 67 | }, 68 | "export": { 69 | "message": "検索エンジンをJSONにエクスポート" 70 | }, 71 | "download": { 72 | "message": "ローカルディスクへのエクスポート" 73 | }, 74 | "header2": { 75 | "message": "ファビコン&アイコン" 76 | }, 77 | "note": { 78 | "message": "この機能にはFirefox 56以上が必要です。" 79 | }, 80 | "header4": { 81 | "message": "検索オプション" 82 | }, 83 | "exactMatch": { 84 | "message": "完全に一致する文字列を検索する" 85 | }, 86 | "gridMode": { 87 | "message": "検索エンジンのリストではなく、アイコンのグリッドを表示する。" 88 | }, 89 | "warning": { 90 | "message": "警告この機能を有効にすると、他の拡張機能のコンテキストメニューが表示されなくなりますのでご注意ください。" 91 | }, 92 | "savePreferences": { 93 | "message": "プリファレンスの保存" 94 | }, 95 | "header6": { 96 | "message": "どの検索エンジンをコンテキストメニューやグリッドに表示させたいですか?" 97 | }, 98 | "clearAll": { 99 | "message": "すべてクリア" 100 | }, 101 | "selectAll": { 102 | "message": "すべて選択" 103 | }, 104 | "sortAlphabetically": { 105 | "message": "アルファベット順に並べ替える" 106 | }, 107 | "showAdvancedFeatures": { 108 | "message": "高度な機能を表示する" 109 | }, 110 | "hideAdvancedFeatures": { 111 | "message": "高度な機能を隠す" 112 | }, 113 | "reset": { 114 | "message": "リセット" 115 | }, 116 | "subheader61": { 117 | "message": "新しい検索エンジンを追加する" 118 | }, 119 | "showSearchEngine": { 120 | "message": "検索エンジンをグリッドまたはコンテキストメニューに表示する" 121 | }, 122 | "searchEngineName": { 123 | "message": "検索エンジン名" 124 | }, 125 | "placeHolderKeyword": { 126 | "message": "キーワード" 127 | }, 128 | "warningText": { 129 | "message": "警告" 130 | }, 131 | "keyboardShortcutWarning": { 132 | "message": "すべてのキーの組み合わせが動作するわけではありません。キーボードショートカットは、macOSではCMD、WindowsやLinuxではCTRLで始まらない場合があります。" 133 | }, 134 | "placeHolderKeyboardShortcut": { 135 | "message": "キーボードショートカット" 136 | }, 137 | "multipleSearchEngines": { 138 | "message": "マルチ検索に含める" 139 | }, 140 | "searchEngineURL": { 141 | "message": "検索エンジンのURL" 142 | }, 143 | "regex": { 144 | "message": "正規表現" 145 | }, 146 | "testSearchEngine": { 147 | "message": "検索エンジンのテスト" 148 | }, 149 | "addSearchEngine": { 150 | "message": "検索エンジンの追加" 151 | }, 152 | "addSeparator": { 153 | "message": "セパレーターの追加" 154 | }, 155 | "clear": { 156 | "message": "クリア" 157 | }, 158 | "disableGrid": { 159 | "message": "アイコンのグリッドを無効にする" 160 | }, 161 | "move": { 162 | "message": "移動" 163 | }, 164 | "up": { 165 | "message": "上" 166 | }, 167 | "down": { 168 | "message": "ダウン" 169 | }, 170 | "remove": { 171 | "message": "削除" 172 | }, 173 | "multipleSearchEnginesSearch": { 174 | "message": "この検索エンジンをマルチ検索に含める" 175 | }, 176 | "notifySavedPreferences": { 177 | "message": "保存された環境設定。" 178 | }, 179 | "notifySearchEngineAdded": { 180 | "message": "検索エンジンが追加された。" 181 | }, 182 | "titleShowEngine": { 183 | "message": "この検索エンジンをコンテキストメニューまたはグリッドに表示する。" 184 | }, 185 | "notifyEnableStorageSync": { 186 | "message": "about:config'で'webextensions.storage.sync.enabled'が'true'に設定されていることを確認してください。" 187 | }, 188 | "notifySearchEnginesLoaded": { 189 | "message": "検索エンジンのデフォルトリストが読み込まれました。" 190 | }, 191 | "notifySearchEngineNotFound": { 192 | "message": "検索エンジンが現在のウェブページで見つからなかった。" 193 | }, 194 | "titleMultipleSearchEngines": { 195 | "message": "複数の検索エンジンによる検索" 196 | }, 197 | "titleAISearch": { 198 | "message": "AIサーチ" 199 | }, 200 | "titleSiteSearch": { 201 | "message": "このサイトを検索する" 202 | }, 203 | "titleOptions": { 204 | "message": "オプション" 205 | }, 206 | "windowTitle": { 207 | "message": "検索結果" 208 | }, 209 | "omniboxDescription": { 210 | "message": "キーワードを含むコンテキスト検索を使って検索する。使用法: cs [keyword] [search terms] (例: 'cs w Linux' はウィキペディアを「Linux」で検索する)" 211 | }, 212 | "notifyUsage": { 213 | "message": "使用法: cs [keyword] [検索語] (例: cs w Linux)" 214 | }, 215 | "notifySearchEngineWithKeyword": { 216 | "message": "キーワード検索エンジン" 217 | }, 218 | "notifyUnknown": { 219 | "message": "は不明である。" 220 | }, 221 | "notifySearchEngineUrlRequired": { 222 | "message": "有効な検索エンジンのURLを入力してください。" 223 | }, 224 | "header7": { 225 | "message": "リセット・オプション" 226 | }, 227 | "sidebar": { 228 | "message": "サイドバー" 229 | }, 230 | "displayFavicons": { 231 | "message": "コンテキストメニューにファビコンを表示する" 232 | }, 233 | "disableAltClick": { 234 | "message": "アイコングリッドを無効にする(Alt + クリック)" 235 | }, 236 | "resetPreferences": { 237 | "message": "環境設定をデフォルトに戻す" 238 | }, 239 | "forceSearchEnginesReload": { 240 | "message": "デフォルト検索エンジンリストの強制再読み込み" 241 | }, 242 | "forceFaviconsReload": { 243 | "message": "ファビコンのリロードを強制する" 244 | }, 245 | "restriction": { 246 | "message": "サイドバーがまだ開いていない場合は、ブラウザのツールバーから「サイドバーを表示」アイコンをクリックしてください。" 247 | }, 248 | "reverseImage": { 249 | "message": "また、逆引き検索結果をサイドバーに表示することはできない。" 250 | }, 251 | "siteSearch": { 252 | "message": "現在のサイトを検索するためのサーチエンジン:" 253 | }, 254 | "header9": { 255 | "message": "高度な機能" 256 | }, 257 | "useRegex": { 258 | "message": "正規表現を使ってコンテキストメニューを作る" 259 | }, 260 | "regexNote": { 261 | "message": "この機能はアイコンのグリッドには適用されない。" 262 | }, 263 | "subheader62": { 264 | "message": "新しいフォルダを追加する" 265 | }, 266 | "folderName": { 267 | "message": "フォルダ名" 268 | }, 269 | "folder": { 270 | "message": "フォルダ" 271 | }, 272 | "addFolder": { 273 | "message": "フォルダを追加" 274 | }, 275 | "keyboardShortcut": { 276 | "message": "キーボードショートカット" 277 | }, 278 | "header10": { 279 | "message": "マルチ検索オプション" 280 | }, 281 | "multisearchIntro": { 282 | "message": "複数の検索結果/タブを開く" 283 | }, 284 | "multiNewWindow": { 285 | "message": "新しいウィンドウで表示" 286 | }, 287 | "multiAfterLastTab": { 288 | "message": "現在のウィンドウの最後のタブの後" 289 | }, 290 | "multiActiveTab": { 291 | "message": "現在のウィンドウでアクティブなタブの直後" 292 | }, 293 | "privacy": { 294 | "message": "新しいウィンドウをプライベートにする" 295 | }, 296 | "reminder": { 297 | "message": "リマインダー" 298 | }, 299 | "enableDownloadsReminder": { 300 | "message": "検索エンジンをエクスポートするには、ダウンロード権限を有効にします。" 301 | }, 302 | "header11": { 303 | "message": "アイコンのグリッドオプション" 304 | }, 305 | "quickIconGrid": { 306 | "message": "テキスト選択後、アイコンのグリッドを即座に表示する。" 307 | }, 308 | "closeGridOnMouseOut": { 309 | "message": "マウスアウトでアイコングリッドを閉じる" 310 | }, 311 | "xOffset": { 312 | "message": "アイコンのグリッドの水平オフセット: " 313 | }, 314 | "yOffset": { 315 | "message": "アイコンのグリッドの垂直オフセット: " 316 | }, 317 | "clearKeyboardShortcuts": { 318 | "message": "すべてのキーボードショートカットを消去する" 319 | }, 320 | "subheader63": { 321 | "message": "新しいAIプロンプトを追加する" 322 | }, 323 | "subheader64": { 324 | "message": "セパレーターの追加" 325 | }, 326 | "testChatGPT": { 327 | "message": "テストAIプロンプト" 328 | }, 329 | "addChatGPTPrompt": { 330 | "message": "AIプロンプトの追加" 331 | }, 332 | "promptShow": { 333 | "message": "グリッドまたはコンテキストメニューにプロンプトを表示する" 334 | }, 335 | "promptName": { 336 | "message": "プロンプト名" 337 | }, 338 | "prompt": { 339 | "message": "プロンプト" 340 | }, 341 | "overwriteSearchEngines": { 342 | "message": "既存の検索エンジンをインポートした検索エンジンで上書きする。" 343 | }, 344 | "removeFolderConfirmPrefix": { 345 | "message": "フォルダを削除してもよろしいですか?" 346 | }, 347 | "removeFolderConfirmSuffix": { 348 | "message": "およびその内容すべて" 349 | }, 350 | "removeSearchEngineConfirm": { 351 | "message": "本当に検索エンジンを削除しますか?" 352 | }, 353 | "popupBlockedMessage": { 354 | "message": "ポップアップがブロックされました!このサイトのポップアップを許可してください。" 355 | }, 356 | "subscriptionOptions": { 357 | "message": "コンテキスト検索購読オプション" 358 | }, 359 | "startNewTrial": { 360 | "message": "新規7日間トライアル開始" 361 | }, 362 | "startSubscription": { 363 | "message": "毎月、毎年、または生涯の新規購読を開始する" 364 | }, 365 | "subscriptionStatus": { 366 | "message": "購読状況" 367 | }, 368 | "notifyMissingSearchEngine": { 369 | "message": "マルチ検索では、検索エンジンは選択されていない。" 370 | }, 371 | "notifyMissingBookmarkUrl": { 372 | "message": "ブックマーク削除用のURLを取得できませんでした" 373 | }, 374 | "notifyAIMinimalRequirements": { 375 | "message": "少なくともAIプロバイダーを選択し、プロンプト名とプロンプトを記入してください。" 376 | }, 377 | "bookmarkPage": { 378 | "message": "このページをブックマークする" 379 | }, 380 | "unbookmarkPage": { 381 | "message": "このページのブックマークを解除する" 382 | }, 383 | "pay": { 384 | "message": "給与" 385 | }, 386 | "startTrial": { 387 | "message": "トライアル開始" 388 | }, 389 | "trialActive": { 390 | "message": "トライアルは活発だ: " 391 | }, 392 | "trialExpired": { 393 | "message": "試用期限切れ" 394 | }, 395 | "subscriptionActive": { 396 | "message": "サブスクリプションアクティブ" 397 | }, 398 | "subscriptionInactive": { 399 | "message": "サブスクリプション非アクティブ" 400 | }, 401 | "noTrialStarted": { 402 | "message": "試験開始なし" 403 | }, 404 | "daysRemaining": { 405 | "message": "残り日数" 406 | }, 407 | "__WET_LOCALE__": { 408 | "message": "ja" 409 | } 410 | } -------------------------------------------------------------------------------- /_locales/ko/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "컨텍스트 검색", 4 | "description": "Name of the extension" 5 | }, 6 | "extensionDescription": { 7 | "message": "확장 프로그램의 환경설정 페이지에서 관리되는 사용자 지정 검색 엔진 및 AI 프롬프트 목록에서 검색 엔진 또는 AI 프롬프트를 사용하여 선택한 텍스트를 검색합니다.", 8 | "description": "Description of the extension" 9 | }, 10 | "multisearch": { 11 | "message": " 는 여러 검색 엔진을 동시에 사용하는 검색입니다.", 12 | "description": "multi-search - it's a search with multiple search engines simultaneously." 13 | }, 14 | "multisearchHowTo": { 15 | "message": "검색 엔진을 포함하려면 가운데 확인란을 선택합니다." 16 | }, 17 | "multisearchLaunch": { 18 | "message": "시작할 수 있습니다." 19 | }, 20 | "multisearchEnd": { 21 | "message": "를 컨텍스트 메뉴에서 클릭하거나 주소 표시줄에서 입력하여 클릭합니다:" 22 | }, 23 | "multisearchEndOfSentence": { 24 | "message": "를 입력한 후 검색어를 입력하세요." 25 | }, 26 | "pageTitle": { 27 | "message": "컨텍스트 검색 환경설정" 28 | }, 29 | "header1": { 30 | "message": "검색 결과는 어디에 표시되나요?" 31 | }, 32 | "newTab": { 33 | "message": "새 탭에서" 34 | }, 35 | "sameTab": { 36 | "message": "같은 탭에서" 37 | }, 38 | "newWindow": { 39 | "message": "새 창에서" 40 | }, 41 | "active": { 42 | "message": "새 탭 또는 창을 활성화합니다." 43 | }, 44 | "position": { 45 | "message": "마지막 탭 뒤에 검색 결과 표시" 46 | }, 47 | "header3": { 48 | "message": "컨텍스트 메뉴의 옵션 위치" 49 | }, 50 | "subheader31": { 51 | "message": "컨텍스트 메뉴에서 옵션 메뉴를 어디에 표시하고 싶으신가요?" 52 | }, 53 | "bottom": { 54 | "message": "하단에서" 55 | }, 56 | "top": { 57 | "message": "상단에서" 58 | }, 59 | "hide": { 60 | "message": "둘 다 없음(옵션 메뉴 숨기기)" 61 | }, 62 | "header5": { 63 | "message": "검색 엔진 가져오기/내보내기" 64 | }, 65 | "import": { 66 | "message": "로컬 디스크에서 JSON 가져오기" 67 | }, 68 | "export": { 69 | "message": "검색 엔진을 JSON으로 내보내기" 70 | }, 71 | "download": { 72 | "message": "로컬 디스크로 내보내기" 73 | }, 74 | "header2": { 75 | "message": "파비콘 및 아이콘" 76 | }, 77 | "note": { 78 | "message": "이 기능을 사용하려면 Firefox 56 이상이 필요합니다." 79 | }, 80 | "header4": { 81 | "message": "검색 옵션" 82 | }, 83 | "exactMatch": { 84 | "message": "정확한 문자열 일치 검색" 85 | }, 86 | "gridMode": { 87 | "message": "검색 엔진 목록 대신 아이콘 그리드 표시" 88 | }, 89 | "warning": { 90 | "message": "경고: 이 기능을 활성화하면 다른 확장 프로그램의 상황에 맞는 메뉴가 표시되지 않는다는 점에 유의하세요." 91 | }, 92 | "savePreferences": { 93 | "message": "환경설정 저장" 94 | }, 95 | "header6": { 96 | "message": "컨텍스트 메뉴 또는 그리드에 어떤 검색 엔진을 표시하고 싶으신가요?" 97 | }, 98 | "clearAll": { 99 | "message": "모두 지우기" 100 | }, 101 | "selectAll": { 102 | "message": "모두 선택" 103 | }, 104 | "sortAlphabetically": { 105 | "message": "알파벳순으로 정렬" 106 | }, 107 | "showAdvancedFeatures": { 108 | "message": "고급 기능 표시" 109 | }, 110 | "hideAdvancedFeatures": { 111 | "message": "고급 기능 숨기기" 112 | }, 113 | "reset": { 114 | "message": "초기화" 115 | }, 116 | "subheader61": { 117 | "message": "새 검색 엔진 추가" 118 | }, 119 | "showSearchEngine": { 120 | "message": "그리드 또는 컨텍스트 메뉴에 검색 엔진 표시" 121 | }, 122 | "searchEngineName": { 123 | "message": "검색 엔진 이름" 124 | }, 125 | "placeHolderKeyword": { 126 | "message": "키워드" 127 | }, 128 | "warningText": { 129 | "message": "경고" 130 | }, 131 | "keyboardShortcutWarning": { 132 | "message": "모든 키 조합이 작동하는 것은 아닙니다. 키보드 단축키는 일반적으로 Firefox 명령용으로 예약되어 있으므로 macOS의 경우 CMD로 시작하거나 Windows 또는 Linux의 경우 CTRL로 시작하지 못할 수 있습니다." 133 | }, 134 | "placeHolderKeyboardShortcut": { 135 | "message": "키보드 단축키" 136 | }, 137 | "multipleSearchEngines": { 138 | "message": "다중 검색에 포함" 139 | }, 140 | "searchEngineURL": { 141 | "message": "검색 엔진 URL" 142 | }, 143 | "regex": { 144 | "message": "정규식" 145 | }, 146 | "testSearchEngine": { 147 | "message": "검색 엔진 테스트" 148 | }, 149 | "addSearchEngine": { 150 | "message": "검색 엔진 추가" 151 | }, 152 | "addSeparator": { 153 | "message": "구분 기호 추가" 154 | }, 155 | "clear": { 156 | "message": "지우기" 157 | }, 158 | "disableGrid": { 159 | "message": "아이콘 그리드 비활성화" 160 | }, 161 | "move": { 162 | "message": "이동" 163 | }, 164 | "up": { 165 | "message": "up" 166 | }, 167 | "down": { 168 | "message": "down" 169 | }, 170 | "remove": { 171 | "message": "제거" 172 | }, 173 | "multipleSearchEnginesSearch": { 174 | "message": "이 검색 엔진을 다중 검색에 포함하세요." 175 | }, 176 | "notifySavedPreferences": { 177 | "message": "환경설정을 저장했습니다." 178 | }, 179 | "notifySearchEngineAdded": { 180 | "message": "검색 엔진이 추가되었습니다." 181 | }, 182 | "titleShowEngine": { 183 | "message": "컨텍스트 메뉴 또는 그리드에 이 검색 엔진을 표시합니다." 184 | }, 185 | "notifyEnableStorageSync": { 186 | "message": "그렇지 않으면 컨텍스트 검색이 작동하지 않으므로 'about:config'에서 'webextensions.storage.sync.enabled' 플래그가 'true'로 설정되어 있는지 확인하세요." 187 | }, 188 | "notifySearchEnginesLoaded": { 189 | "message": "기본 검색 엔진 목록이 로드되었습니다." 190 | }, 191 | "notifySearchEngineNotFound": { 192 | "message": "현재 웹 페이지에서 검색 엔진을 찾을 수 없습니다." 193 | }, 194 | "titleMultipleSearchEngines": { 195 | "message": "여러 검색 엔진 검색" 196 | }, 197 | "titleAISearch": { 198 | "message": "AI 검색" 199 | }, 200 | "titleSiteSearch": { 201 | "message": "이 사이트에서 다음을 사용하여 검색" 202 | }, 203 | "titleOptions": { 204 | "message": "옵션" 205 | }, 206 | "windowTitle": { 207 | "message": "검색 결과" 208 | }, 209 | "omniboxDescription": { 210 | "message": "키워드로 컨텍스트 검색을 사용하여 검색합니다. 사용법: cs [키워드] [검색어] (예: 'cs w Linux'는 'Linux'라는 용어로 Wikipedia를 검색합니다.)" 211 | }, 212 | "notifyUsage": { 213 | "message": "사용법: cs [키워드] [검색어](예: cs w Linux)" 214 | }, 215 | "notifySearchEngineWithKeyword": { 216 | "message": "키워드로 검색 엔진" 217 | }, 218 | "notifyUnknown": { 219 | "message": "를 알 수 없습니다." 220 | }, 221 | "notifySearchEngineUrlRequired": { 222 | "message": "유효한 검색 엔진 URL을 입력하세요." 223 | }, 224 | "header7": { 225 | "message": "재설정 옵션" 226 | }, 227 | "sidebar": { 228 | "message": "사이드바에서" 229 | }, 230 | "displayFavicons": { 231 | "message": "컨텍스트 메뉴에 파비콘 표시" 232 | }, 233 | "disableAltClick": { 234 | "message": "아이콘 그리드 비활성화(Alt + 클릭)" 235 | }, 236 | "resetPreferences": { 237 | "message": "환경설정을 기본 설정으로 재설정" 238 | }, 239 | "forceSearchEnginesReload": { 240 | "message": "기본 검색 엔진 목록 강제 새로 고침" 241 | }, 242 | "forceFaviconsReload": { 243 | "message": "페이비콘 강제로 다시 로드하기" 244 | }, 245 | "restriction": { 246 | "message": "사이드바가 아직 열려 있지 않은 경우 브라우저 도구 모음에서 '사이드바 표시' 아이콘을 클릭해야 할 수 있습니다." 247 | }, 248 | "reverseImage": { 249 | "message": "또한 역방향 이미지 검색 결과는 사이드바에 표시할 수 없습니다." 250 | }, 251 | "siteSearch": { 252 | "message": "현재 사이트 검색을 위한 검색 엔진입니다:" 253 | }, 254 | "header9": { 255 | "message": "고급 기능" 256 | }, 257 | "useRegex": { 258 | "message": "정규식을 사용하여 컨텍스트 메뉴 구축하기" 259 | }, 260 | "regexNote": { 261 | "message": "이 기능은 아이콘 그리드에는 적용되지 않습니다." 262 | }, 263 | "subheader62": { 264 | "message": "새 폴더 추가" 265 | }, 266 | "folderName": { 267 | "message": "폴더 이름" 268 | }, 269 | "folder": { 270 | "message": "폴더" 271 | }, 272 | "addFolder": { 273 | "message": "폴더 추가" 274 | }, 275 | "keyboardShortcut": { 276 | "message": "키보드 단축키" 277 | }, 278 | "header10": { 279 | "message": "다중 검색 옵션" 280 | }, 281 | "multisearchIntro": { 282 | "message": "여러 검색 결과/탭 열기" 283 | }, 284 | "multiNewWindow": { 285 | "message": "새 창에서" 286 | }, 287 | "multiAfterLastTab": { 288 | "message": "현재 창의 마지막 탭 다음에" 289 | }, 290 | "multiActiveTab": { 291 | "message": "현재 창의 활성 탭 바로 뒤" 292 | }, 293 | "privacy": { 294 | "message": "새 창을 비공개로 설정" 295 | }, 296 | "reminder": { 297 | "message": "알림" 298 | }, 299 | "enableDownloadsReminder": { 300 | "message": "다운로드 권한을 활성화하여 검색 엔진을 내보낼 수 있습니다." 301 | }, 302 | "header11": { 303 | "message": "아이콘 그리드 옵션" 304 | }, 305 | "quickIconGrid": { 306 | "message": "텍스트를 선택한 후 아이콘 그리드를 즉시 표시합니다." 307 | }, 308 | "closeGridOnMouseOut": { 309 | "message": "마우스 아웃 시 아이콘 그리드 닫기" 310 | }, 311 | "xOffset": { 312 | "message": "아이콘 그리드의 수평 오프셋: " 313 | }, 314 | "yOffset": { 315 | "message": "아이콘 그리드의 수직 오프셋: " 316 | }, 317 | "clearKeyboardShortcuts": { 318 | "message": "모든 키보드 단축키 지우기" 319 | }, 320 | "subheader63": { 321 | "message": "새로운 AI 프롬프트 추가" 322 | }, 323 | "subheader64": { 324 | "message": "구분 기호 추가" 325 | }, 326 | "testChatGPT": { 327 | "message": "AI 프롬프트 테스트" 328 | }, 329 | "addChatGPTPrompt": { 330 | "message": "AI 프롬프트 추가" 331 | }, 332 | "promptShow": { 333 | "message": "그리드 또는 컨텍스트 메뉴에 프롬프트 표시" 334 | }, 335 | "promptName": { 336 | "message": "프롬프트 이름" 337 | }, 338 | "prompt": { 339 | "message": "프롬프트" 340 | }, 341 | "overwriteSearchEngines": { 342 | "message": "기존 검색 엔진을 가져온 검색 엔진으로 덮어쓰기" 343 | }, 344 | "removeFolderConfirmPrefix": { 345 | "message": "폴더를 제거하시겠습니까?" 346 | }, 347 | "removeFolderConfirmSuffix": { 348 | "message": "및 모든 콘텐츠" 349 | }, 350 | "removeSearchEngineConfirm": { 351 | "message": "검색 엔진을 제거하시겠습니까?" 352 | }, 353 | "popupBlockedMessage": { 354 | "message": "팝업이 차단되었습니다! 이 사이트의 팝업을 허용해 주세요." 355 | }, 356 | "subscriptionOptions": { 357 | "message": "컨텍스트 검색 구독 옵션" 358 | }, 359 | "startNewTrial": { 360 | "message": "새로운 7일 평가판 시작하기" 361 | }, 362 | "startSubscription": { 363 | "message": "월간, 연간 또는 평생 구독을 새로 시작하세요." 364 | }, 365 | "subscriptionStatus": { 366 | "message": "구독 상태" 367 | }, 368 | "notifyMissingSearchEngine": { 369 | "message": "다중 검색을 위해 선택한 검색 엔진이 없습니다." 370 | }, 371 | "notifyMissingBookmarkUrl": { 372 | "message": "북마크 제거를 위한 URL을 검색할 수 없습니다." 373 | }, 374 | "notifyAIMinimalRequirements": { 375 | "message": "최소한 AI 제공업체를 선택하고 프롬프트 이름과 프롬프트를 입력하세요." 376 | }, 377 | "bookmarkPage": { 378 | "message": "이 페이지 북마크하기" 379 | }, 380 | "unbookmarkPage": { 381 | "message": "이 페이지 북마크 해제" 382 | }, 383 | "pay": { 384 | "message": "결제" 385 | }, 386 | "startTrial": { 387 | "message": "평가판 시작" 388 | }, 389 | "trialActive": { 390 | "message": "평가판이 활성화되었습니다: " 391 | }, 392 | "trialExpired": { 393 | "message": "평가판 만료" 394 | }, 395 | "subscriptionActive": { 396 | "message": "구독: 활성" 397 | }, 398 | "subscriptionInactive": { 399 | "message": "구독: 비활성" 400 | }, 401 | "noTrialStarted": { 402 | "message": "시작되지 않은 평가판" 403 | }, 404 | "daysRemaining": { 405 | "message": "남은 일수" 406 | }, 407 | "__WET_LOCALE__": { 408 | "message": "ko" 409 | } 410 | } -------------------------------------------------------------------------------- /_locales/zh_cn/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "上下文搜索", 4 | "description": "Name of the extension" 5 | }, 6 | "extensionDescription": { 7 | "message": "从扩展首选项页面管理的搜索引擎和人工智能提示自定义列表中,使用搜索引擎或人工智能提示搜索所选文本。", 8 | "description": "Description of the extension" 9 | }, 10 | "multisearch": { 11 | "message": " 是指同时使用多个搜索引擎进行搜索。", 12 | "description": "multi-search - it's a search with multiple search engines simultaneously." 13 | }, 14 | "multisearchHowTo": { 15 | "message": "选中中间的复选框,将搜索引擎包含在" 16 | }, 17 | "multisearchLaunch": { 18 | "message": "您可以启动" 19 | }, 20 | "multisearchEnd": { 21 | "message": "或从地址栏中键入:" 22 | }, 23 | "multisearchEndOfSentence": { 24 | "message": "然后是您的搜索条件。" 25 | }, 26 | "pageTitle": { 27 | "message": "上下文搜索首选项" 28 | }, 29 | "header1": { 30 | "message": "搜索结果应该出现在哪里?" 31 | }, 32 | "newTab": { 33 | "message": "在新标签页中" 34 | }, 35 | "sameTab": { 36 | "message": "在同一选项卡中" 37 | }, 38 | "newWindow": { 39 | "message": "在新窗口中" 40 | }, 41 | "active": { 42 | "message": "使新标签页或窗口处于活动状态" 43 | }, 44 | "position": { 45 | "message": "在最后一个选项卡之后显示搜索结果" 46 | }, 47 | "header3": { 48 | "message": "选项在右键菜单中的位置" 49 | }, 50 | "subheader31": { 51 | "message": "您希望选项菜单出现在右键菜单的什么位置?" 52 | }, 53 | "bottom": { 54 | "message": "在底部" 55 | }, 56 | "top": { 57 | "message": "在顶部" 58 | }, 59 | "hide": { 60 | "message": "都不是(隐藏选项菜单)" 61 | }, 62 | "header5": { 63 | "message": "导入/导出搜索引擎" 64 | }, 65 | "import": { 66 | "message": "从本地磁盘导入 JSON" 67 | }, 68 | "export": { 69 | "message": "将搜索引擎导出为 JSON" 70 | }, 71 | "download": { 72 | "message": "导出到本地磁盘" 73 | }, 74 | "header2": { 75 | "message": "收藏夹和图标" 76 | }, 77 | "note": { 78 | "message": "此功能需要 Firefox 56 或以上版本。" 79 | }, 80 | "header4": { 81 | "message": "搜索选项" 82 | }, 83 | "exactMatch": { 84 | "message": "搜索完全匹配的字符串" 85 | }, 86 | "gridMode": { 87 | "message": "显示图标网格,而不是搜索引擎列表" 88 | }, 89 | "warning": { 90 | "message": "警告:请注意,启用此功能将无法显示其他扩展程序的上下文菜单。" 91 | }, 92 | "savePreferences": { 93 | "message": "保存首选项" 94 | }, 95 | "header6": { 96 | "message": "您希望哪些搜索引擎出现在右键菜单或网格中?" 97 | }, 98 | "clearAll": { 99 | "message": "全部清除" 100 | }, 101 | "selectAll": { 102 | "message": "全部选择" 103 | }, 104 | "sortAlphabetically": { 105 | "message": "按字母顺序排序" 106 | }, 107 | "showAdvancedFeatures": { 108 | "message": "显示高级功能" 109 | }, 110 | "hideAdvancedFeatures": { 111 | "message": "隐藏高级功能" 112 | }, 113 | "reset": { 114 | "message": "重置" 115 | }, 116 | "subheader61": { 117 | "message": "添加新的搜索引擎" 118 | }, 119 | "showSearchEngine": { 120 | "message": "在网格或右键菜单中显示搜索引擎" 121 | }, 122 | "searchEngineName": { 123 | "message": "搜索引擎名称" 124 | }, 125 | "placeHolderKeyword": { 126 | "message": "关键词" 127 | }, 128 | "warningText": { 129 | "message": "警告" 130 | }, 131 | "keyboardShortcutWarning": { 132 | "message": "并非所有组合键都能使用。键盘快捷键可能不会以 macOS 上的 CMD 或 Windows 或 Linux 上的 CTRL 开头,因为这些通常是为 Firefox 命令保留的。" 133 | }, 134 | "placeHolderKeyboardShortcut": { 135 | "message": "键盘快捷键" 136 | }, 137 | "multipleSearchEngines": { 138 | "message": "包含在多重搜索中" 139 | }, 140 | "searchEngineURL": { 141 | "message": "搜索引擎 URL" 142 | }, 143 | "regex": { 144 | "message": "正则表达式" 145 | }, 146 | "testSearchEngine": { 147 | "message": "测试搜索引擎" 148 | }, 149 | "addSearchEngine": { 150 | "message": "添加搜索引擎" 151 | }, 152 | "addSeparator": { 153 | "message": "添加分隔符" 154 | }, 155 | "clear": { 156 | "message": "清晰" 157 | }, 158 | "disableGrid": { 159 | "message": "禁用图标网格" 160 | }, 161 | "move": { 162 | "message": "移动" 163 | }, 164 | "up": { 165 | "message": "向上" 166 | }, 167 | "down": { 168 | "message": "下来" 169 | }, 170 | "remove": { 171 | "message": "移除" 172 | }, 173 | "multipleSearchEnginesSearch": { 174 | "message": "在多重搜索中包含该搜索引擎" 175 | }, 176 | "notifySavedPreferences": { 177 | "message": "保存的首选项。" 178 | }, 179 | "notifySearchEngineAdded": { 180 | "message": "增加了搜索引擎。" 181 | }, 182 | "titleShowEngine": { 183 | "message": "在右键菜单或网格中显示该搜索引擎" 184 | }, 185 | "notifyEnableStorageSync": { 186 | "message": "请确保在 \"about:config \"中将标志 \"webextensions.storage.sync.enabled \"设置为 \"true\",否则 \"上下文搜索 \"将无法运行。" 187 | }, 188 | "notifySearchEnginesLoaded": { 189 | "message": "已加载默认搜索引擎列表。" 190 | }, 191 | "notifySearchEngineNotFound": { 192 | "message": "在当前网页上无法找到搜索引擎" 193 | }, 194 | "titleMultipleSearchEngines": { 195 | "message": "多个搜索引擎搜索" 196 | }, 197 | "titleAISearch": { 198 | "message": "人工智能搜索" 199 | }, 200 | "titleSiteSearch": { 201 | "message": "搜索本网站" 202 | }, 203 | "titleOptions": { 204 | "message": "选项" 205 | }, 206 | "windowTitle": { 207 | "message": "搜索结果" 208 | }, 209 | "omniboxDescription": { 210 | "message": "使用带关键字的上下文搜索进行搜索。使用方法:cs [关键词] [搜索条件](例如,\"cs w Linux \"可搜索维基百科中的 \"Linux\")。" 211 | }, 212 | "notifyUsage": { 213 | "message": "使用方法:cs [keyword] [search terms] (例如:cs w Linux)" 214 | }, 215 | "notifySearchEngineWithKeyword": { 216 | "message": "关键词搜索引擎" 217 | }, 218 | "notifyUnknown": { 219 | "message": "未知。" 220 | }, 221 | "notifySearchEngineUrlRequired": { 222 | "message": "请填写有效的搜索引擎 URL。" 223 | }, 224 | "header7": { 225 | "message": "重置选项" 226 | }, 227 | "sidebar": { 228 | "message": "侧边栏" 229 | }, 230 | "displayFavicons": { 231 | "message": "在右键菜单中显示收藏夹" 232 | }, 233 | "disableAltClick": { 234 | "message": "禁用图标网格(Alt + 点击)" 235 | }, 236 | "resetPreferences": { 237 | "message": "将首选项重置为默认设置" 238 | }, 239 | "forceSearchEnginesReload": { 240 | "message": "强制重新加载默认搜索引擎列表" 241 | }, 242 | "forceFaviconsReload": { 243 | "message": "强制重新加载收藏夹" 244 | }, 245 | "restriction": { 246 | "message": "如果侧边栏还没有打开,则可能需要点击浏览器工具栏上的 \"显示侧边栏 \"图标。" 247 | }, 248 | "reverseImage": { 249 | "message": "此外,侧边栏不能显示反向图像搜索结果。" 250 | }, 251 | "siteSearch": { 252 | "message": "用于搜索当前网站的搜索引擎:" 253 | }, 254 | "header9": { 255 | "message": "高级功能" 256 | }, 257 | "useRegex": { 258 | "message": "使用正则表达式创建上下文菜单" 259 | }, 260 | "regexNote": { 261 | "message": "此功能不适用于图标网格。" 262 | }, 263 | "subheader62": { 264 | "message": "添加新文件夹" 265 | }, 266 | "folderName": { 267 | "message": "文件夹名称" 268 | }, 269 | "folder": { 270 | "message": "资料夹" 271 | }, 272 | "addFolder": { 273 | "message": "添加文件夹" 274 | }, 275 | "keyboardShortcut": { 276 | "message": "键盘快捷键" 277 | }, 278 | "header10": { 279 | "message": "多重搜索选项" 280 | }, 281 | "multisearchIntro": { 282 | "message": "打开多个搜索结果/标签" 283 | }, 284 | "multiNewWindow": { 285 | "message": "在新窗口中" 286 | }, 287 | "multiAfterLastTab": { 288 | "message": "当前窗口中最后一个选项卡之后" 289 | }, 290 | "multiActiveTab": { 291 | "message": "紧跟当前窗口中的活动选项卡" 292 | }, 293 | "privacy": { 294 | "message": "将新窗口设为私有" 295 | }, 296 | "reminder": { 297 | "message": "提醒" 298 | }, 299 | "enableDownloadsReminder": { 300 | "message": "启用下载权限以导出搜索引擎。" 301 | }, 302 | "header11": { 303 | "message": "图标网格选项" 304 | }, 305 | "quickIconGrid": { 306 | "message": "选择文本后立即显示图标网格" 307 | }, 308 | "closeGridOnMouseOut": { 309 | "message": "鼠标移出时关闭图标网格" 310 | }, 311 | "xOffset": { 312 | "message": "图标网格的水平偏移: " 313 | }, 314 | "yOffset": { 315 | "message": "图标网格的垂直偏移量: " 316 | }, 317 | "clearKeyboardShortcuts": { 318 | "message": "清除所有键盘快捷键" 319 | }, 320 | "subheader63": { 321 | "message": "添加新的人工智能提示" 322 | }, 323 | "subheader64": { 324 | "message": "添加分隔符" 325 | }, 326 | "testChatGPT": { 327 | "message": "测试 AI 提示" 328 | }, 329 | "addChatGPTPrompt": { 330 | "message": "添加人工智能提示" 331 | }, 332 | "promptShow": { 333 | "message": "在网格或右键菜单中显示提示" 334 | }, 335 | "promptName": { 336 | "message": "提示名称" 337 | }, 338 | "prompt": { 339 | "message": "提示" 340 | }, 341 | "overwriteSearchEngines": { 342 | "message": "用导入的搜索引擎覆盖现有搜索引擎" 343 | }, 344 | "removeFolderConfirmPrefix": { 345 | "message": "您确定要删除文件夹" 346 | }, 347 | "removeFolderConfirmSuffix": { 348 | "message": "及其所有内容" 349 | }, 350 | "removeSearchEngineConfirm": { 351 | "message": "您确定要移除搜索引擎吗?" 352 | }, 353 | "popupBlockedMessage": { 354 | "message": "弹出窗口已阻止!请允许弹出窗口访问本网站。" 355 | }, 356 | "subscriptionOptions": { 357 | "message": "上下文搜索订阅选项" 358 | }, 359 | "startNewTrial": { 360 | "message": "开始新的 7 天试用" 361 | }, 362 | "startSubscription": { 363 | "message": "开始新的包月、包年或终身订购" 364 | }, 365 | "subscriptionStatus": { 366 | "message": "订阅状态" 367 | }, 368 | "notifyMissingSearchEngine": { 369 | "message": "没有为多重搜索选择任何搜索引擎" 370 | }, 371 | "notifyMissingBookmarkUrl": { 372 | "message": "无法检索删除书签的 URL" 373 | }, 374 | "notifyAIMinimalRequirements": { 375 | "message": "请至少选择一个人工智能提供商,并提供一个提示符名称和一个提示符" 376 | }, 377 | "bookmarkPage": { 378 | "message": "收藏本页" 379 | }, 380 | "unbookmarkPage": { 381 | "message": "取消书签" 382 | }, 383 | "pay": { 384 | "message": "薪酬" 385 | }, 386 | "startTrial": { 387 | "message": "开始试用" 388 | }, 389 | "trialActive": { 390 | "message": "试验正在进行中: " 391 | }, 392 | "trialExpired": { 393 | "message": "试用期已过" 394 | }, 395 | "subscriptionActive": { 396 | "message": "订阅:激活" 397 | }, 398 | "subscriptionInactive": { 399 | "message": "订阅:不活动" 400 | }, 401 | "noTrialStarted": { 402 | "message": "未开始试验" 403 | }, 404 | "daysRemaining": { 405 | "message": "剩余天数" 406 | }, 407 | "__WET_LOCALE__": { 408 | "message": "zh_cn" 409 | } 410 | } -------------------------------------------------------------------------------- /_locales/zh_tw/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Context Search", 4 | "description": "Name of the extension" 5 | }, 6 | "extensionDescription": { 7 | "message": "使用自訂搜尋引擎清單中的搜尋引擎或 AI 提示搜尋選取的文字,搜尋引擎和 AI 提示在擴充套件的偏好設定頁面中管理。", 8 | "description": "Description of the extension" 9 | }, 10 | "multisearch": { 11 | "message": " 是同時使用多個搜尋引擎進行搜尋。", 12 | "description": "multi-search - it's a search with multiple search engines simultaneously." 13 | }, 14 | "multisearchHowTo": { 15 | "message": "勾選中間的核取方塊,以將搜尋引擎包含在" 16 | }, 17 | "multisearchLaunch": { 18 | "message": "您可以啟動" 19 | }, 20 | "multisearchEnd": { 21 | "message": "從上下文功能表或從位址列輸入:" 22 | }, 23 | "multisearchEndOfSentence": { 24 | "message": "接著是您的搜尋字串。" 25 | }, 26 | "pageTitle": { 27 | "message": "上下文搜尋偏好設定" 28 | }, 29 | "header1": { 30 | "message": "搜尋結果應該出現在哪裡?" 31 | }, 32 | "newTab": { 33 | "message": "在新標籤中" 34 | }, 35 | "sameTab": { 36 | "message": "在同一個標籤中" 37 | }, 38 | "newWindow": { 39 | "message": "在新視窗中" 40 | }, 41 | "active": { 42 | "message": "使新的標籤頁或視窗處於活動狀態" 43 | }, 44 | "position": { 45 | "message": "在最後一個索引標籤後顯示搜尋結果" 46 | }, 47 | "header3": { 48 | "message": "選項在上下文功能表中的位置" 49 | }, 50 | "subheader31": { 51 | "message": "您希望選項功能表出現在上下文功能表的哪個位置?" 52 | }, 53 | "bottom": { 54 | "message": "在底部" 55 | }, 56 | "top": { 57 | "message": "在頂端" 58 | }, 59 | "hide": { 60 | "message": "兩者皆非(隱藏選項功能表)" 61 | }, 62 | "header5": { 63 | "message": "輸入/輸出搜尋引擎" 64 | }, 65 | "import": { 66 | "message": "從本機磁碟匯入 JSON" 67 | }, 68 | "export": { 69 | "message": "匯出搜尋引擎至 JSON" 70 | }, 71 | "download": { 72 | "message": "匯出至本機磁碟" 73 | }, 74 | "header2": { 75 | "message": "首頁圖示與圖示" 76 | }, 77 | "note": { 78 | "message": "此功能需要 Firefox 56 或以上版本。" 79 | }, 80 | "header4": { 81 | "message": "搜尋選項" 82 | }, 83 | "exactMatch": { 84 | "message": "搜尋完全符合的字串" 85 | }, 86 | "gridMode": { 87 | "message": "顯示圖示方格而非搜尋引擎清單" 88 | }, 89 | "warning": { 90 | "message": "警告:請注意,啟用此功能將無法顯示其他擴充套件的上下文功能表。" 91 | }, 92 | "savePreferences": { 93 | "message": "儲存偏好設定" 94 | }, 95 | "header6": { 96 | "message": "您希望哪些搜尋引擎出現在上下文功能表或網格中?" 97 | }, 98 | "clearAll": { 99 | "message": "全部清除" 100 | }, 101 | "selectAll": { 102 | "message": "全部選擇" 103 | }, 104 | "sortAlphabetically": { 105 | "message": "按字母順序排序" 106 | }, 107 | "showAdvancedFeatures": { 108 | "message": "顯示進階功能" 109 | }, 110 | "hideAdvancedFeatures": { 111 | "message": "隱藏進階功能" 112 | }, 113 | "reset": { 114 | "message": "重設" 115 | }, 116 | "subheader61": { 117 | "message": "新增搜尋引擎" 118 | }, 119 | "showSearchEngine": { 120 | "message": "在網格或上下文功能表中顯示搜尋引擎" 121 | }, 122 | "searchEngineName": { 123 | "message": "搜尋引擎名稱" 124 | }, 125 | "placeHolderKeyword": { 126 | "message": "關鍵字" 127 | }, 128 | "warningText": { 129 | "message": "警告" 130 | }, 131 | "keyboardShortcutWarning": { 132 | "message": "並非所有按鍵組合都適用。鍵盤捷徑可能不會以 macOS 上的 CMD 或 Windows 或 Linux 上的 CTRL 開頭,因為這些通常是預留給 Firefox 指令的。" 133 | }, 134 | "placeHolderKeyboardShortcut": { 135 | "message": "鍵盤捷徑" 136 | }, 137 | "multipleSearchEngines": { 138 | "message": "包含在多重搜尋中" 139 | }, 140 | "searchEngineURL": { 141 | "message": "搜尋引擎 URL" 142 | }, 143 | "regex": { 144 | "message": "正規表達式" 145 | }, 146 | "testSearchEngine": { 147 | "message": "測試搜尋引擎" 148 | }, 149 | "addSearchEngine": { 150 | "message": "新增搜尋引擎" 151 | }, 152 | "addSeparator": { 153 | "message": "新增分隔線" 154 | }, 155 | "clear": { 156 | "message": "清除" 157 | }, 158 | "disableGrid": { 159 | "message": "停用圖示方格" 160 | }, 161 | "move": { 162 | "message": "移動" 163 | }, 164 | "up": { 165 | "message": "向上" 166 | }, 167 | "down": { 168 | "message": "下" 169 | }, 170 | "remove": { 171 | "message": "移除" 172 | }, 173 | "multipleSearchEnginesSearch": { 174 | "message": "在多重搜尋中包含此搜尋引擎" 175 | }, 176 | "notifySavedPreferences": { 177 | "message": "儲存的偏好設定。" 178 | }, 179 | "notifySearchEngineAdded": { 180 | "message": "新增搜尋引擎。" 181 | }, 182 | "titleShowEngine": { 183 | "message": "在上下文功能表或網格中顯示此搜尋引擎" 184 | }, 185 | "notifyEnableStorageSync": { 186 | "message": "請確認「about:config」中的標誌「webextensions.storage.sync.enabled」設為「true」,否則 Context Search 將無法運作。" 187 | }, 188 | "notifySearchEnginesLoaded": { 189 | "message": "已載入預設搜尋引擎清單。" 190 | }, 191 | "notifySearchEngineNotFound": { 192 | "message": "在目前的網頁上找不到搜尋引擎" 193 | }, 194 | "titleMultipleSearchEngines": { 195 | "message": "多種搜尋引擎搜尋" 196 | }, 197 | "titleAISearch": { 198 | "message": "AI 搜尋" 199 | }, 200 | "titleSiteSearch": { 201 | "message": "搜尋本網站" 202 | }, 203 | "titleOptions": { 204 | "message": "選項" 205 | }, 206 | "windowTitle": { 207 | "message": "搜尋結果" 208 | }, 209 | "omniboxDescription": { 210 | "message": "使用關鍵字的上下文搜尋。使用方法:cs [keyword] [search terms] (例如 'cs w Linux「 在 Wikipedia 搜尋詞彙 」Linux')" 211 | }, 212 | "notifyUsage": { 213 | "message": "使用方法:cs [keyword] [search terms] (例如:cs w Linux)" 214 | }, 215 | "notifySearchEngineWithKeyword": { 216 | "message": "搜尋引擎與關鍵字" 217 | }, 218 | "notifyUnknown": { 219 | "message": "未知。" 220 | }, 221 | "notifySearchEngineUrlRequired": { 222 | "message": "請填入有效的搜尋引擎 URL。" 223 | }, 224 | "header7": { 225 | "message": "重設選項" 226 | }, 227 | "sidebar": { 228 | "message": "在側邊欄中" 229 | }, 230 | "displayFavicons": { 231 | "message": "在上下文功能表中顯示收藏夾" 232 | }, 233 | "disableAltClick": { 234 | "message": "停用圖示方格 (Alt + 按一下)" 235 | }, 236 | "resetPreferences": { 237 | "message": "將偏好設定重設為預設設定" 238 | }, 239 | "forceSearchEnginesReload": { 240 | "message": "強制重新載入預設搜尋引擎清單" 241 | }, 242 | "forceFaviconsReload": { 243 | "message": "強制重新載入收藏夾" 244 | }, 245 | "restriction": { 246 | "message": "如果側邊欄尚未開啟,那麼您可能需要點選瀏覽器工具列上的「顯示側邊欄」圖示。" 247 | }, 248 | "reverseImage": { 249 | "message": "此外,側邊欄也無法顯示反向圖片搜尋結果。" 250 | }, 251 | "siteSearch": { 252 | "message": "搜尋目前網站的搜尋引擎:" 253 | }, 254 | "header9": { 255 | "message": "進階功能" 256 | }, 257 | "useRegex": { 258 | "message": "使用正則表達式建立上下文功能表" 259 | }, 260 | "regexNote": { 261 | "message": "此功能不適用於圖示方格。" 262 | }, 263 | "subheader62": { 264 | "message": "新增資料夾" 265 | }, 266 | "folderName": { 267 | "message": "資料夾名稱" 268 | }, 269 | "folder": { 270 | "message": "資料夾" 271 | }, 272 | "addFolder": { 273 | "message": "新增資料夾" 274 | }, 275 | "keyboardShortcut": { 276 | "message": "鍵盤捷徑" 277 | }, 278 | "header10": { 279 | "message": "多重搜尋選項" 280 | }, 281 | "multisearchIntro": { 282 | "message": "開啟多個搜尋結果/標籤" 283 | }, 284 | "multiNewWindow": { 285 | "message": "在新視窗中" 286 | }, 287 | "multiAfterLastTab": { 288 | "message": "在目前視窗的最後一個索引標籤之後" 289 | }, 290 | "multiActiveTab": { 291 | "message": "緊接目前視窗中的作用中標籤" 292 | }, 293 | "privacy": { 294 | "message": "將新視窗設為隱私" 295 | }, 296 | "reminder": { 297 | "message": "提醒事項" 298 | }, 299 | "enableDownloadsReminder": { 300 | "message": "啟用下載權限以匯出您的搜尋引擎。" 301 | }, 302 | "header11": { 303 | "message": "圖示網格選項" 304 | }, 305 | "quickIconGrid": { 306 | "message": "選取文字後立即顯示圖示方格" 307 | }, 308 | "closeGridOnMouseOut": { 309 | "message": "滑鼠移出時關閉圖示方格" 310 | }, 311 | "xOffset": { 312 | "message": "圖示方格的水平偏移: " 313 | }, 314 | "yOffset": { 315 | "message": "圖示方格的垂直偏移: " 316 | }, 317 | "clearKeyboardShortcuts": { 318 | "message": "清除所有鍵盤捷徑" 319 | }, 320 | "subheader63": { 321 | "message": "新增 AI 提示" 322 | }, 323 | "subheader64": { 324 | "message": "新增分隔線" 325 | }, 326 | "testChatGPT": { 327 | "message": "測試 AI 提示" 328 | }, 329 | "addChatGPTPrompt": { 330 | "message": "新增 AI 提示" 331 | }, 332 | "promptShow": { 333 | "message": "在網格或上下文功能表中顯示提示" 334 | }, 335 | "promptName": { 336 | "message": "提示名稱" 337 | }, 338 | "prompt": { 339 | "message": "提示" 340 | }, 341 | "overwriteSearchEngines": { 342 | "message": "使用匯入的搜尋引擎覆寫現有的搜尋引擎" 343 | }, 344 | "removeFolderConfirmPrefix": { 345 | "message": "您確定要移除資料夾" 346 | }, 347 | "removeFolderConfirmSuffix": { 348 | "message": "及其所有內容" 349 | }, 350 | "removeSearchEngineConfirm": { 351 | "message": "您確定要移除搜尋引擎嗎?" 352 | }, 353 | "popupBlockedMessage": { 354 | "message": "彈出視窗已封鎖!請允許本網站的彈出窗口。" 355 | }, 356 | "subscriptionOptions": { 357 | "message": "內容搜尋訂閱選項" 358 | }, 359 | "startNewTrial": { 360 | "message": "開始新的 7 天試用" 361 | }, 362 | "startSubscription": { 363 | "message": "開始新的按月、按年或終身訂購" 364 | }, 365 | "subscriptionStatus": { 366 | "message": "訂閱狀態" 367 | }, 368 | "notifyMissingSearchEngine": { 369 | "message": "未選擇搜尋引擎進行多重搜尋" 370 | }, 371 | "notifyMissingBookmarkUrl": { 372 | "message": "無法檢索移除書籤的 URL" 373 | }, 374 | "notifyAIMinimalRequirements": { 375 | "message": "請至少選擇一個 AI 提供者,並提供提示名稱和提示" 376 | }, 377 | "bookmarkPage": { 378 | "message": "將此頁加入書籤" 379 | }, 380 | "unbookmarkPage": { 381 | "message": "取消書籤本頁" 382 | }, 383 | "pay": { 384 | "message": "薪資" 385 | }, 386 | "startTrial": { 387 | "message": "開始試用" 388 | }, 389 | "trialActive": { 390 | "message": "試用中: " 391 | }, 392 | "trialExpired": { 393 | "message": "試用已過期" 394 | }, 395 | "subscriptionActive": { 396 | "message": "訂閱:活躍" 397 | }, 398 | "subscriptionInactive": { 399 | "message": "訂閱:無效" 400 | }, 401 | "noTrialStarted": { 402 | "message": "未開始試驗" 403 | }, 404 | "daysRemaining": { 405 | "message": "剩餘天數" 406 | }, 407 | "__WET_LOCALE__": { 408 | "message": "zh_tw" 409 | } 410 | } -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | // Get the current directory 5 | const currentDir = path.resolve(); 6 | 7 | // List of files/directories to exclude from the build 8 | const excludeList = [ 9 | 'build', 10 | 'crowdin.yml', 11 | 'node_modules', 12 | 'web-ext-artifacts', 13 | 'web-ext-config.js', 14 | 'package.json', 15 | 'package-lock.json', 16 | 'build.js', 17 | 'manifest.chrome.json', 18 | 'manifest.firefox.json' 19 | ]; 20 | 21 | // Function to ensure directory exists 22 | function ensureDir(dirPath) { 23 | if (!fs.existsSync(dirPath)) { 24 | fs.mkdirSync(dirPath, { recursive: true }); 25 | } 26 | } 27 | 28 | // Function to copy a file 29 | function copyFile(src, dest, browser) { 30 | // Skip browser-polyfill.js for Firefox 31 | if (browser === 'firefox' && path.basename(src) === 'browser-polyfill.js') { 32 | return; 33 | } 34 | 35 | try { 36 | fs.copyFileSync(src, dest); 37 | } catch (error) { 38 | if (error.code !== 'ENOTSUP') { 39 | throw error; 40 | } 41 | // If file is a socket or other special file, skip it 42 | console.warn(`Skipping file ${src} due to unsupported file type`); 43 | } 44 | } 45 | 46 | // Function to process HTML files 47 | function processHtmlFile(src, dest, browser) { 48 | let content = fs.readFileSync(src, 'utf8'); 49 | 50 | if (browser === 'firefox') { 51 | // Remove browser-polyfill.min.js script tag for Firefox 52 | content = content.replace(/ 30 | 31 | 32 | -------------------------------------------------------------------------------- /html/addSearchEngineOrFolder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Add Search Engine or Folder 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 17 | 18 |
19 |
20 |

Add a new search engine

21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 | 33 | 35 | 36 |
37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 |

Add a new AI prompt

45 |
46 |
47 | 48 | 59 | 60 | 62 | 63 | 64 | 65 |
66 |
67 | 68 | 69 | 70 |
71 |
72 |
73 |
74 | 75 | 76 |
77 |
78 |

Add a separator

79 |
80 |
81 | 82 |
83 |
84 |
85 |
86 | 87 | 88 |
89 |
90 |

Add a new folder

91 |
92 |
93 | 94 | 96 | 97 |
98 |
99 | 100 | 101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /html/bookmark.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Bookmark active tab to Context Search 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 |
33 | 34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /html/bookmarkRemoval.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Confirm bookmark removal 8 | 9 | 10 | 11 | 12 | 13 |

Do you want to remove the bookmark from Context Search?

14 |
15 |
16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /html/bookmarks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Bookmarks Search 9 | 10 | 15 | 16 | 17 | 18 |

Bookmarks Search Results

19 |
    20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /html/history.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | History Search 9 | 10 | 15 | 16 | 17 | 18 |

    History Search Results

    19 |
      20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /html/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AI Search 8 | 9 | 10 | 11 | 12 | 13 |
      14 |
      15 | 16 |
      17 |

      AI Search engines available: chatgpt, claude, gemini, grok, perplexity, poe, andi, you

      18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /html/sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Context Search 10 | 11 | 12 | 13 |
      14 | 15 |
      16 | 17 | 18 | -------------------------------------------------------------------------------- /html/subscription_choice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Context Search Subscription Options 9 | 50 | 51 | 52 | 53 |
      54 | 55 | 56 |
      57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /html/subscription_status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Subscription Status 9 | 32 | 33 | 34 | 35 |
      36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /icons/bookmark-grey-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/bookmark-red-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/context-search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_128.png -------------------------------------------------------------------------------- /icons/icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_16.png -------------------------------------------------------------------------------- /icons/icon_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_20.png -------------------------------------------------------------------------------- /icons/icon_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_24.png -------------------------------------------------------------------------------- /icons/icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_256.png -------------------------------------------------------------------------------- /icons/icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_32.png -------------------------------------------------------------------------------- /icons/icon_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_48.png -------------------------------------------------------------------------------- /icons/icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_512.png -------------------------------------------------------------------------------- /icons/icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/icon_64.png -------------------------------------------------------------------------------- /icons/search-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/search-icon.png -------------------------------------------------------------------------------- /icons/subscription-status-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/subscription-status-icon.png -------------------------------------------------------------------------------- /icons/uxwing-minus-red-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Layer 1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /icons/uxwing-plus-green-icon.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/icons/uxwing-plus-green-icon.svg -------------------------------------------------------------------------------- /images/searchEngineDescription.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/odebroqueville/contextSearch/8d84e4a3ca56e785ccdc04b51786dfac548989d1/images/searchEngineDescription.png -------------------------------------------------------------------------------- /manifest.chrome.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "__MSG_extensionName__", 4 | "description": "__MSG_extensionDescription__", 5 | "version": "6.1.0", 6 | "default_locale": "en", 7 | "homepage_url": "https://github.com/odebroqueville/contextSearch", 8 | "author": "Olivier de Broqueville", 9 | "content_security_policy": { 10 | "extension_pages": "script-src 'self'; object-src 'self'; img-src 'self' data:;" 11 | }, 12 | "action": { 13 | "default_icon": "/icons/icon_32.png", 14 | "default_title": "Bookmark the active tab to Context Search" 15 | }, 16 | "background": { 17 | "service_worker": "cs_service_worker.js", 18 | "type": "module" 19 | }, 20 | "side_panel": { 21 | "default_path": "html/sidebar.html" 22 | }, 23 | "permissions": [ 24 | "alarms", 25 | "contextMenus", 26 | "declarativeNetRequestWithHostAccess", 27 | "scripting", 28 | "search", 29 | "sidePanel", 30 | "storage", 31 | "tabs" 32 | ], 33 | "optional_permissions": [ 34 | "bookmarks", 35 | "clipboardWrite", 36 | "downloads", 37 | "history", 38 | "notifications" 39 | ], 40 | "host_permissions": [ 41 | "" 42 | ], 43 | "omnibox": { 44 | "keyword": "cs" 45 | }, 46 | "commands": { 47 | "launch-icons-grid": { 48 | "suggested_key": { 49 | "default": "Alt+J", 50 | "mac": "Alt+J" 51 | }, 52 | "description": "Launch the icons grid" 53 | }, 54 | "open-popup": { 55 | "suggested_key": { 56 | "default": "Alt+K", 57 | "mac": "Alt+K" 58 | }, 59 | "description": "Open the popup window" 60 | } 61 | }, 62 | "icons": { 63 | "16": "icons/icon_16.png", 64 | "20": "icons/icon_20.png", 65 | "24": "icons/icon_24.png", 66 | "32": "icons/icon_32.png", 67 | "48": "icons/icon_48.png", 68 | "64": "icons/icon_64.png", 69 | "128": "icons/icon_128.png", 70 | "256": "icons/icon_256.png", 71 | "512": "icons/icon_512.png" 72 | }, 73 | "options_ui": { 74 | "page": "/html/options.html", 75 | "open_in_tab": true 76 | }, 77 | "content_scripts": [ 78 | { 79 | "matches": [ 80 | "http://*/*", 81 | "https://*/*" 82 | ], 83 | "js": [ 84 | "/libs/browser-polyfill.min.js", 85 | "/scripts/selection.js" 86 | ] 87 | }, 88 | { 89 | "matches": [ 90 | "http://*/*#_sidebar", 91 | "https://*/*#_sidebar" 92 | ], 93 | "css": [ 94 | "/styles/sidebar.css" 95 | ], 96 | "run_at": "document_start", 97 | "all_frames": true 98 | } 99 | ], 100 | "web_accessible_resources": [ 101 | { 102 | "resources": [ 103 | "/libs/browser-polyfill.min.js", 104 | "/scripts/selection.js" 105 | ], 106 | "matches": [ 107 | "" 108 | ] 109 | } 110 | ] 111 | } -------------------------------------------------------------------------------- /manifest.firefox.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "default_locale": "en", 4 | "name": "__MSG_extensionName__", 5 | "description": "__MSG_extensionDescription__", 6 | "version": "6.1.0", 7 | "homepage_url": "https://github.com/odebroqueville/contextSearch", 8 | "author": "Olivier de Broqueville", 9 | "browser_specific_settings": { 10 | "gecko": { 11 | "id": "contextsearch.help@gmail.com", 12 | "strict_min_version": "128.0" 13 | } 14 | }, 15 | "content_security_policy": { 16 | "extension_pages": "script-src 'self'; object-src 'self'; img-src 'self' data:;" 17 | }, 18 | "action": { 19 | "default_icon": "/icons/icon_32.png", 20 | "default_title": "Bookmark the active tab to Context Search" 21 | }, 22 | "sidebar_action": { 23 | "default_icon": { 24 | "16": "icons/context-search.svg", 25 | "32": "icons/context-search.svg" 26 | }, 27 | "default_title": "Search results", 28 | "default_panel": "/html/sidebar.html", 29 | "open_at_install": false 30 | }, 31 | "background": { 32 | "scripts": [ 33 | "cs_service_worker.js" 34 | ], 35 | "type": "module" 36 | }, 37 | "permissions": [ 38 | "alarms", 39 | "contextMenus", 40 | "declarativeNetRequestWithHostAccess", 41 | "menus", 42 | "scripting", 43 | "storage", 44 | "search", 45 | "tabs" 46 | ], 47 | "optional_permissions": [ 48 | "bookmarks", 49 | "clipboardWrite", 50 | "downloads", 51 | "history", 52 | "notifications" 53 | ], 54 | "host_permissions": [ 55 | "" 56 | ], 57 | "omnibox": { 58 | "keyword": "cs" 59 | }, 60 | "commands": { 61 | "launch-icons-grid": { 62 | "suggested_key": { 63 | "default": "Ctrl+Shift+J", 64 | "mac": "Ctrl+Shift+J" 65 | }, 66 | "description": "Launch the icons grid" 67 | }, 68 | "open-popup": { 69 | "suggested_key": { 70 | "default": "Ctrl+Shift+K", 71 | "mac": "Ctrl+Shift+K" 72 | }, 73 | "description": "Open the popup window" 74 | } 75 | }, 76 | "icons": { 77 | "16": "icons/icon_16.png", 78 | "20": "icons/icon_20.png", 79 | "24": "icons/icon_24.png", 80 | "32": "icons/icon_32.png", 81 | "48": "icons/icon_48.png", 82 | "64": "icons/icon_64.png", 83 | "128": "icons/icon_128.png", 84 | "256": "icons/icon_256.png", 85 | "512": "icons/icon_512.png" 86 | }, 87 | "options_ui": { 88 | "page": "/html/options.html", 89 | "open_in_tab": true 90 | }, 91 | "content_scripts": [ 92 | { 93 | "matches": [ 94 | "http://*/*", 95 | "https://*/*" 96 | ], 97 | "js": [ 98 | "/scripts/selection.js" 99 | ] 100 | }, 101 | { 102 | "matches": [ 103 | "http://*/*#_sidebar", 104 | "https://*/*#_sidebar" 105 | ], 106 | "css": [ 107 | "/styles/sidebar.css" 108 | ], 109 | "run_at": "document_start", 110 | "all_frames": true 111 | } 112 | ], 113 | "web_accessible_resources": [ 114 | { 115 | "resources": [ 116 | "/scripts/selection.js" 117 | ], 118 | "matches": [ 119 | "" 120 | ] 121 | } 122 | ] 123 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contextsearch", 3 | "title": "Context Search", 4 | "version": "6.1.0", 5 | "description": "Firefox add-on to search selected text in a web page using your favorite search engines.", 6 | "browserslist": [ 7 | "Firefox >= 128" 8 | ], 9 | "scripts": { 10 | "build": "web-ext build", 11 | "build:chrome": "node build.js chrome", 12 | "build:firefox": "node build.js firefox", 13 | "build:all": "node build.js", 14 | "test": "eslint .", 15 | "lint": "eslint ." 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/odebroqueville/contextSearch.git" 20 | }, 21 | "keywords": [ 22 | "webextension", 23 | "context", 24 | "context search", 25 | "context menu", 26 | "search", 27 | "search engine" 28 | ], 29 | "author": "Olivier de Broqueville ", 30 | "license": "EULA", 31 | "bugs": { 32 | "url": "https://github.com/odebroqueville/contextSearch/issues" 33 | }, 34 | "homepage": "https://github.com/odebroqueville/contextSearch#readme", 35 | "dependencies": { 36 | "extpay": "^3.1.1", 37 | "sortablejs": "^1.15.6", 38 | "webextension-polyfill": "^0.12.0" 39 | }, 40 | "devDependencies": { 41 | "eslint": "^8.57.1" 42 | } 43 | } -------------------------------------------------------------------------------- /scripts/addSearchEngineForPostRequest.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | 4 | // Add event listeners to the Cancel and OK buttons. 5 | document.getElementById('cancel').addEventListener('click', closeModal); 6 | document.getElementById('ok').addEventListener('click', submitForm); 7 | 8 | async function submitForm() { 9 | const searchEngineName = document.getElementById('searchEngineName').value; 10 | const keyword = document.getElementById('keyword').value; 11 | const keyboardShortcut = document.getElementById('keyboardShortcut').value; 12 | 13 | // You can perform further actions with the form data, for example: 14 | console.log("Search Engine Name:", searchEngineName); 15 | console.log("Keyword:", keyword); 16 | console.log("Keyboard Shortcut:", keyboardShortcut); 17 | 18 | // Send the data back to the background script 19 | await browser.runtime.sendMessage({ action: 'addNewPostSearchEngine', data: { searchEngineName, keyword, keyboardShortcut } }); 20 | 21 | closeModal(); 22 | } 23 | 24 | function closeModal() { 25 | window.close(); 26 | } -------------------------------------------------------------------------------- /scripts/bookmark.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | 4 | // Import constants to use STORAGE_KEYS 5 | import { STORAGE_KEYS } from './constants.js'; 6 | 7 | /// Global variables 8 | let logToConsole = false; 9 | let meta = 'win+'; 10 | let os = 'Windows'; 11 | 12 | document.addEventListener('DOMContentLoaded', async () => { 13 | // Retrieve the parent window ID from the URL parameters 14 | const urlParams = new URLSearchParams(window.location.search); 15 | const parentWindowId = parseInt(urlParams.get('parentWindowId')); 16 | const cancelButton = document.getElementById('cancelButton'); 17 | const addBookmarkButton = document.getElementById('addBookmarkButton'); 18 | const folderSelect = document.getElementById('folder'); 19 | const title = document.getElementById('title'); 20 | const url = document.getElementById('url'); 21 | const inputKeyboardShortcut = document.getElementById('keyboardShortcut'); 22 | let keysPressed = {}; 23 | const searchEngines = await getStoredData(STORAGE_KEYS.SEARCH_ENGINES); 24 | const tabs = await browser.tabs.query({ active: true, windowId: parentWindowId }); 25 | const activeTab = tabs[0]; 26 | 27 | // Determine the operating system and define the meta key 28 | os = await getOS(); 29 | initMetaKey(); 30 | 31 | // --- Load logToConsole from storage --- 32 | try { 33 | // Fetch using the correct key directly 34 | const data = await browser.storage.local.get(STORAGE_KEYS.LOG_TO_CONSOLE); 35 | // Check if the key exists and is a boolean 36 | if (typeof data[STORAGE_KEYS.LOG_TO_CONSOLE] === 'boolean') { 37 | logToConsole = data[STORAGE_KEYS.LOG_TO_CONSOLE]; 38 | } 39 | } catch (error) { 40 | console.error("Error loading logToConsole setting from storage:", error); 41 | // Keep the default value if loading fails 42 | } 43 | // -------------------------------------- 44 | 45 | if (logToConsole) console.log(searchEngines); 46 | if (logToConsole) console.log(activeTab); 47 | if (logToConsole) console.log(os); 48 | 49 | // Fill in default values for the title & url 50 | title.value = activeTab.title; 51 | url.value = activeTab.url; 52 | 53 | // Event handlers for adding a keyboard shortcut 54 | inputKeyboardShortcut.addEventListener('keyup', (e) => { 55 | if (Object.keys(keysPressed).length > 0) { 56 | inputKeyboardShortcut.value = getKeyboardShortcut(e, keysPressed, os,); 57 | } 58 | keysPressed = {}; 59 | }); 60 | inputKeyboardShortcut.addEventListener('keydown', (e) => { 61 | if (logToConsole) console.log(e); 62 | if (e.target.nodeName !== 'INPUT') return; 63 | if ((os === 'macOS' && e.metaKey) || ((os === 'Windows' || os === 'Linux') && e.ctrlKey) || (!isInFocus(e.target)) || (e.key === 'Escape')) { 64 | if (logToConsole) console.log("Keys pressed: " + keysPressed); 65 | keysPressed = {}; 66 | return; 67 | } 68 | const key = e.key; 69 | if (isKeyAllowed(e)) keysPressed[key] = e.code; 70 | if (logToConsole) console.log(keysPressed); 71 | }); 72 | 73 | // Cancel button closes the popup 74 | cancelButton.addEventListener('click', () => { 75 | window.close(); 76 | }); 77 | 78 | // Add Bookmark button functionality 79 | addBookmarkButton.addEventListener('click', async () => { 80 | if (isSupportedProtocol(url.value)) { 81 | await addBookmark(folderSelect, searchEngines); 82 | window.close(); 83 | } else { 84 | // Display an error message for 4 seconds if the URL is not supported 85 | const warning = document.getElementById('warning'); 86 | setTimeout(() => { 87 | warning.style.color = 'red'; 88 | warning.style.weight = 'bold'; 89 | warning.textContent = 'Invalid URL. Please enter a valid bookmark URL.'; 90 | }, 4000); 91 | warning.textContent = ''; 92 | } 93 | }); 94 | 95 | // Populate the folder select with bookmark folders 96 | populateFolderSelect(folderSelect, searchEngines); 97 | }); 98 | 99 | // Detect the underlying OS 100 | async function getOS() { 101 | try { 102 | // Send message to background script to get OS 103 | const api = typeof browser !== 'undefined' ? browser : chrome; 104 | const { os } = await api.runtime.sendMessage({ action: "getOS" }); 105 | if (os && os !== "Unknown") return os; 106 | 107 | // Fallback to user agent if background script detection fails 108 | const ua = navigator.userAgent.toLowerCase(); 109 | if (ua.includes('mac os x')) return 'macOS'; 110 | if (ua.includes('windows')) return 'Windows'; 111 | if (ua.includes('android')) return 'Android'; 112 | if (ua.includes('linux')) return 'Linux'; 113 | if (ua.includes('iphone') || ua.includes('ipad') || ua.includes('ipod')) return 'iOS'; 114 | return null; 115 | } catch (error) { 116 | console.error('Error detecting OS:', error); 117 | return null; 118 | } 119 | } 120 | 121 | function populateFolderSelect(selectElement, searchEngines) { 122 | for (const id in searchEngines) { 123 | if (searchEngines[id].isFolder) { 124 | const option = document.createElement('option'); 125 | option.value = id; 126 | option.text = searchEngines[id].name; 127 | selectElement.appendChild(option); 128 | } 129 | } 130 | } 131 | 132 | // Add the bookmark to the selected folder 133 | async function addBookmark(folderSelect, searchEngines) { 134 | const title = document.getElementById('title').value; 135 | const url = document.getElementById('url').value; 136 | const keyword = document.getElementById('keyword').value; 137 | const keyboardShortcut = document.getElementById('keyboardShortcut').value; 138 | const folderId = folderSelect.value; 139 | const children = searchEngines[folderId].children; 140 | const i = children.length; 141 | 142 | // Generate a unique id for the bookmark 143 | let id = 'link-' + title.trim().replaceAll(' ', '-').toLowerCase(); 144 | id = id.substring(0, 25); // Limit id length to 25 characters 145 | while (!isIdUnique(id, searchEngines)) { 146 | id = 'link-' + title.trim().replaceAll(' ', '-').toLowerCase() + '-' + Math.floor(Math.random() * 1000000000000); 147 | } 148 | 149 | // Add the bookmark id to the children array of the selected folder 150 | children.push(id); 151 | 152 | // Define bookmark to be added to search engines list 153 | searchEngines[id] = {}; 154 | searchEngines[id]['index'] = i; 155 | searchEngines[id]['name'] = title; 156 | searchEngines[id]['keyword'] = keyword; 157 | searchEngines[id]['keyboardShortcut'] = keyboardShortcut; 158 | searchEngines[id]['multitab'] = false; 159 | searchEngines[id]['url'] = url; 160 | searchEngines[id]['show'] = true; 161 | //searchEngines[id]['imageFormat'] = imageFormat; 162 | //searchEngines[id]['base64'] = base64String; 163 | 164 | await sendMessage('saveSearchEngines', searchEngines); 165 | } 166 | 167 | // Function to check if a key is allowed 168 | function isKeyAllowed(event) { 169 | const disallowedKeys = [ 170 | 'Tab', 'Enter', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 171 | 'Escape', ' ', 'Delete', 'Backspace', 'Home', 'End', 172 | 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 173 | ]; 174 | 175 | return !disallowedKeys.includes(event.key); 176 | } 177 | 178 | // Function to check if an elementis in focus 179 | function isInFocus(element) { 180 | return (document.activeElement === element); 181 | } 182 | 183 | // Ensure the ID generated is unique 184 | function isIdUnique(testId, searchEngines) { 185 | for (let id in searchEngines) { 186 | if (id === testId) { 187 | return false; 188 | } 189 | } 190 | return true; 191 | } 192 | 193 | function isSupportedProtocol(urlString) { 194 | const supportedProtocols = ["https:", "http:", "ftp:", "file:", "javascript:"]; 195 | const url = document.createElement('a'); 196 | url.href = urlString; 197 | return supportedProtocols.indexOf(url.protocol) !== -1; 198 | } 199 | 200 | // Handle the input of a keyboard shortcut for a search engine in the Options page 201 | function getKeyboardShortcut(e, keysPressed, os) { 202 | if (logToConsole) console.log(e); 203 | if (e.target.nodeName !== 'INPUT' || !isKeyAllowed(e)) return; 204 | // If the ESC key is pressed or the CMD key is pressed on macOS or CTRL key is pressed on Windows or Linux, then do nothing 205 | if ((os === 'macOS' && e.metaKey) || ((os === 'Windows' || os === 'Linux') && e.ctrlKey) || (!isInFocus(e.target)) || (e.key === 'Escape')) { 206 | if (logToConsole) console.log("Keys pressed: " + Object.keys(keysPressed)); 207 | keysPressed = {}; 208 | return; 209 | } 210 | e.preventDefault(); 211 | 212 | if (logToConsole) console.log(os); 213 | if (logToConsole) console.log(keysPressed); 214 | 215 | let keyboardShortcut = ''; 216 | 217 | // Identify modifier keys pressed 218 | for (let key in keysPressed) { 219 | switch (key) { 220 | case 'Control': 221 | keyboardShortcut = keyboardShortcut + 'ctrl+'; 222 | delete keysPressed[key]; 223 | break; 224 | case 'Alt': 225 | keyboardShortcut = keyboardShortcut + 'alt+'; 226 | delete keysPressed[key]; 227 | break; 228 | case 'Shift': 229 | keyboardShortcut = keyboardShortcut + 'shift+'; 230 | delete keysPressed[key]; 231 | break; 232 | case 'Meta': 233 | keyboardShortcut = keyboardShortcut + meta; 234 | delete keysPressed[key]; 235 | break; 236 | default: 237 | } 238 | } 239 | if (logToConsole) console.log(`Modifier keys pressed: ${keyboardShortcut}`); 240 | if (logToConsole) console.log(`Remaining non-modifier keys pressed: `); 241 | if (logToConsole) console.log(keysPressed); 242 | 243 | // Identify the remaining non-modifier keys pressed 244 | for (let key in keysPressed) { 245 | if (os === 'macOS') { 246 | keyboardShortcut += keysPressed[key].substring(3).toLowerCase(); 247 | } else { 248 | keyboardShortcut += key.toLowerCase(); 249 | } 250 | } 251 | 252 | // Save the identified keyboard shortcut 253 | if (logToConsole) console.log(keyboardShortcut); 254 | return keyboardShortcut; 255 | } 256 | 257 | // Function to get stored data 258 | async function getStoredData(key) { 259 | try { 260 | if (key) { 261 | const result = await browser.storage.local.get(key); 262 | if (logToConsole) console.log(`Getting ${key} from storage:`, result[key]); 263 | return result[key]; 264 | } else { 265 | const result = await browser.storage.local.get(); 266 | if (logToConsole) console.log('Getting all data from storage:', result); 267 | return result; 268 | } 269 | } catch (error) { 270 | console.error(`Error getting ${key} from storage:`, error); 271 | return null; 272 | } 273 | } 274 | 275 | // Send a message to the background script 276 | async function sendMessage(action, data) { 277 | await browser.runtime.sendMessage({ action: action, data: JSON.parse(JSON.stringify(data)) }) 278 | .catch(e => { 279 | if (logToConsole) console.error(e); 280 | }); 281 | } 282 | 283 | // Initialize meta key based on OS 284 | function initMetaKey() { 285 | if (os === 'macOS') { 286 | meta = 'cmd+'; 287 | } else if (os === 'Windows') { 288 | meta = 'win+'; 289 | } else if (os === 'Linux') { 290 | meta = 'super+'; 291 | } else { 292 | meta = 'meta+'; 293 | } 294 | } -------------------------------------------------------------------------------- /scripts/bookmarkRemoval.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | 4 | /// Import constants to use STORAGE_KEYS 5 | import { STORAGE_KEYS } from './constants.js'; 6 | 7 | /// Global variables 8 | let logToConsole = false; 9 | 10 | document.addEventListener('DOMContentLoaded', async () => { 11 | // Focus on the No button by default to prevent accidental removal of a bookmark 12 | document.getElementById("noBtn").focus(); 13 | 14 | // Retrieve the parent tab's url from the URL parameters 15 | const urlParams = new URLSearchParams(window.location.search); 16 | const url = urlParams.get('url'); 17 | 18 | // Get the buttons 19 | const yesBtn = document.getElementById("yesBtn"); 20 | const noBtn = document.getElementById("noBtn"); 21 | 22 | const options = await getStoredData(STORAGE_KEYS.OPTIONS); 23 | const searchEngines = await getStoredData(STORAGE_KEYS.SEARCH_ENGINES); 24 | 25 | logToConsole = options.logToConsole; 26 | 27 | // Handle Yes button click 28 | yesBtn.onclick = async () => { 29 | for (const id in searchEngines) { 30 | if (id.startsWith('link-') && searchEngines[id].url && searchEngines[id].url === url) { 31 | delete searchEngines[id]; 32 | await sendMessage('saveSearchEngines', searchEngines); 33 | window.close(); 34 | break; 35 | } 36 | } 37 | // If no matching bookmark found, display a warning message for 4 seconds 38 | const warning = document.getElementById("warning"); 39 | setTimeout(() => { 40 | warning.style.color = 'red'; 41 | warning.style.weight = 'bold'; 42 | warning.textContent = 'The bookmark could not be found in Context Search'; 43 | }, 4000); 44 | warning.textContent = ''; 45 | window.close(); 46 | } 47 | 48 | // Handle No button click 49 | noBtn.onclick = () => { 50 | window.close(); 51 | } 52 | }); 53 | 54 | // Send a message to the background script 55 | async function sendMessage(action, data) { 56 | await browser.runtime.sendMessage({ action: action, data: JSON.parse(JSON.stringify(data)) }) 57 | .catch(e => { 58 | if (logToConsole) console.error(e); 59 | }); 60 | } 61 | 62 | // Function to get stored data 63 | async function getStoredData(key) { 64 | try { 65 | if (key) { 66 | const result = await browser.storage.local.get(key); 67 | if (logToConsole) console.log(`Getting ${key} from storage:`, result[key]); 68 | return result[key]; 69 | } else { 70 | const result = await browser.storage.local.get(); 71 | if (logToConsole) console.log('Getting all data from storage:', result); 72 | return result; 73 | } 74 | } catch (error) { 75 | console.error(`Error getting ${key} from storage:`, error); 76 | return null; 77 | } 78 | } -------------------------------------------------------------------------------- /scripts/bookmarks.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | 4 | document.addEventListener('DOMContentLoaded', getHistoryItems); 5 | 6 | async function getHistoryItems() { 7 | const bookmarkItems = (await browser.storage.local.get('bookmarkItems')).bookmarkItems; 8 | const searchTerms = (await browser.storage.local.get('searchTerms')).searchTerms; 9 | await browser.storage.local.remove(['bookmarkItems', 'searchTerms']); 10 | const ol = document.getElementById('bookmarkItems'); 11 | const h2 = document.getElementById('title'); 12 | if (searchTerms && searchTerms !== "recent") { 13 | h2.textContent = "Bookmarks search results for " + searchTerms; 14 | } else if (searchTerms === "recent") { 15 | h2.textContent = "Recent bookmarks"; 16 | } else { 17 | h2.textContent = "All bookmarks"; 18 | } 19 | console.log(bookmarkItems); 20 | console.log(searchTerms); 21 | for (let item of bookmarkItems) { 22 | const da = "Date added: " + new Date(item.dateAdded); 23 | let li = document.createElement('li'); 24 | li.style.margin = '0px'; 25 | li.style.padding = '0px'; 26 | let title = document.createElement('h3'); 27 | title.textContent = item.title; 28 | let url = document.createElement('p'); 29 | url.textContent = item.url; 30 | let dateAdded = document.createElement('p'); 31 | dateAdded.textContent = da; 32 | li.appendChild(title); 33 | li.appendChild(url); 34 | li.appendChild(dateAdded); 35 | ol.appendChild(li); 36 | } 37 | } -------------------------------------------------------------------------------- /scripts/constants.js: -------------------------------------------------------------------------------- 1 | /// Constants 2 | 3 | // Debug mode 4 | export const DEBUG = true; 5 | 6 | // User agent for sidebar search results 7 | const USER_AGENT_FOR_SIDEBAR = 8 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/605.1.15'; 9 | const USER_AGENT_FOR_GOOGLE = 'Mozilla/5.0 (Linux; Android 13; G8VOU Build/TP1A.220905.004;wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chromium/104.0.5112.97; Mobile Safari/537.36'; 10 | 11 | export const BACKUP_ALARM_NAME = 'serviceWorkerMaintenance'; 12 | 13 | // File containing default list of search engines 14 | export const DEFAULT_SEARCH_ENGINES = 'defaultSearchEngines.json'; 15 | 16 | // Persistent data keys 17 | export const STORAGE_KEYS = { 18 | OPTIONS: 'options', 19 | SEARCH_ENGINES: 'searchEngines', 20 | NOTIFICATIONS_ENABLED: 'notificationsEnabled', 21 | LOG_TO_CONSOLE: 'logToConsole', 22 | BOOKMARKS: 'bookmarkItems', 23 | HISTORY: 'historyItems', 24 | SEARCH_TERMS: 'searchTerms', 25 | SELECTION: 'selection', 26 | TARGET_URL: 'targetUrl' 27 | }; 28 | 29 | // Constants for SortableJS configuration 30 | export const SORTABLE_BASE_OPTIONS = { 31 | group: { 32 | name: 'folder', 33 | pull: true, 34 | put: true 35 | }, 36 | animation: 150, 37 | filter: 'input, textarea', 38 | preventOnFilter: false 39 | }; 40 | 41 | // Rules for modifying User-Agent headers based on URL patterns 42 | export const HEADER_RULES = [ 43 | { 44 | // Rule for Google image search 45 | id: 1, 46 | priority: 2, 47 | action: { 48 | type: 'modifyHeaders', 49 | requestHeaders: [ 50 | { 51 | header: 'User-Agent', 52 | operation: 'set', 53 | value: USER_AGENT_FOR_GOOGLE 54 | } 55 | ] 56 | }, 57 | condition: { 58 | urlFilter: '.*google\\.com/.*(searchbyimage|tbs=sbi:|webhp.*tbs=sbi:).*#_sidebar', 59 | resourceTypes: ['main_frame', 'sub_frame'] 60 | } 61 | }, 62 | { 63 | // Rule for YouTube 64 | id: 2, 65 | priority: 2, 66 | action: { 67 | type: 'modifyHeaders', 68 | requestHeaders: [ 69 | { 70 | header: 'User-Agent', 71 | operation: 'set', 72 | value: USER_AGENT_FOR_GOOGLE 73 | } 74 | ] 75 | }, 76 | condition: { 77 | urlFilter: '||youtube.com/*#_sidebar', 78 | resourceTypes: ['main_frame', 'sub_frame'] 79 | } 80 | }, 81 | { 82 | // Default rule for sidebar mode (lowest priority) 83 | id: 3, 84 | priority: 1, 85 | action: { 86 | type: 'modifyHeaders', 87 | requestHeaders: [ 88 | { 89 | header: 'User-Agent', 90 | operation: 'set', 91 | value: USER_AGENT_FOR_SIDEBAR 92 | }, 93 | { 94 | header: 'Accept', 95 | operation: 'set', 96 | value: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' 97 | }, 98 | { 99 | header: 'Accept-Language', 100 | operation: 'set', 101 | value: 'en-US,en;q=0.9' 102 | }, 103 | { 104 | header: 'X-Requested-With', 105 | operation: 'set', 106 | value: 'Mobile' 107 | }, 108 | { 109 | header: 'Sec-CH-UA-Mobile', 110 | operation: 'set', 111 | value: '?1' 112 | }, 113 | { 114 | header: 'Sec-CH-UA-Platform', 115 | operation: 'set', 116 | value: '"iOS"' 117 | }, 118 | { 119 | header: 'DNT', 120 | operation: 'set', 121 | value: '1' 122 | }, 123 | { 124 | header: 'Viewport-Width', 125 | operation: 'set', 126 | value: '375' 127 | }, 128 | { 129 | header: 'Width', 130 | operation: 'set', 131 | value: '375' 132 | } 133 | ] 134 | }, 135 | condition: { 136 | urlFilter: '*#_sidebar', 137 | excludedRequestDomains: ['lens.google.com'], 138 | resourceTypes: ['main_frame', 'sub_frame'] 139 | } 140 | } 141 | ]; 142 | 143 | // Constants for translations 144 | export const titleMultipleSearchEngines = browser.i18n.getMessage( 145 | 'titleMultipleSearchEngines' 146 | ); 147 | export const titleAISearch = browser.i18n.getMessage('titleAISearch'); 148 | export const titleSiteSearch = browser.i18n.getMessage('titleSiteSearch'); 149 | export const titleExactMatch = browser.i18n.getMessage('exactMatch'); 150 | export const titleOptions = browser.i18n.getMessage('titleOptions'); 151 | export const windowTitle = browser.i18n.getMessage('windowTitle'); 152 | export const omniboxDescription = browser.i18n.getMessage('omniboxDescription'); 153 | export const notifySearchEnginesLoaded = browser.i18n.getMessage( 154 | 'notifySearchEnginesLoaded' 155 | ); 156 | export const notifySearchEngineNotFound = browser.i18n.getMessage( 157 | 'notifySearchEngineNotFound' 158 | ); 159 | export const notifySearchEngineAdded = browser.i18n.getMessage( 160 | 'notifySearchEngineAdded' 161 | ); 162 | export const notifyUsage = browser.i18n.getMessage('notifyUsage'); 163 | export const notifySearchEngineWithKeyword = browser.i18n.getMessage( 164 | 'notifySearchEngineWithKeyword' 165 | ); 166 | export const notifyUnknown = browser.i18n.getMessage('notifyUnknown'); 167 | export const notifySearchEngineUrlRequired = browser.i18n.getMessage( 168 | 'notifySearchEngineUrlRequired' 169 | ); 170 | export const notifyMissingSearchEngine = browser.i18n.getMessage('notifyMissingSearchEngine'); 171 | export const notifyMissingBookmarkUrl = browser.i18n.getMessage('notifyMissingBookmarkUrl'); 172 | export const notifyAIMinimalRequirements = browser.i18n.getMessage('notifyAIMinimalRequirements'); 173 | export const bookmarkPage = browser.i18n.getMessage('bookmarkPage'); 174 | export const unbookmarkPage = browser.i18n.getMessage('unbookmarkPage'); 175 | export const addSearchEngine = browser.i18n.getMessage('addSearchEngine'); 176 | export const subscriptionStatus = browser.i18n.getMessage('subscriptionStatus'); 177 | export const pay = browser.i18n.getMessage('pay'); 178 | export const startTrial = browser.i18n.getMessage('startTrial'); 179 | export const startNewTrial = browser.i18n.getMessage('startNewTrial'); 180 | export const trialActive = browser.i18n.getMessage('trialActive'); 181 | export const trialExpired = browser.i18n.getMessage('trialExpired'); 182 | export const subscriptionActive = browser.i18n.getMessage('subscriptionActive'); 183 | export const subscriptionInactive = browser.i18n.getMessage('subscriptionInactive'); 184 | export const noTrialStarted = browser.i18n.getMessage('noTrialStarted'); 185 | export const daysRemaining = browser.i18n.getMessage('daysRemaining'); 186 | 187 | // Default settings 188 | export const DEFAULT_OPTIONS = { 189 | exactMatch: false, 190 | tabMode: 'openNewTab', 191 | optionsMenuLocation: 'bottom', 192 | tabActive: false, 193 | lastTab: false, 194 | displayFavicons: true, 195 | quickIconGrid: false, 196 | closeGridOnMouseOut: true, 197 | offsetX: 12, 198 | offsetY: 12, 199 | disableAltClick: false, 200 | forceSearchEnginesReload: false, 201 | resetPreferences: false, 202 | forceFaviconsReload: false, 203 | siteSearch: 'Google', 204 | siteSearchUrl: 'https://www.google.com/search?q=', 205 | multiMode: 'multiNewWindow', 206 | privateMode: false, 207 | overwriteSearchEngines: false 208 | }; 209 | 210 | // Configuration for option updates 211 | export const UPDATE_CONFIG = { 212 | searchOptions: { 213 | fields: ['exactMatch'], 214 | requiresMenuRebuild: true 215 | }, 216 | displayFavicons: { 217 | fields: ['displayFavicons'], 218 | requiresMenuRebuild: true 219 | }, 220 | quickIconGrid: { 221 | fields: ['quickIconGrid'], 222 | requiresMenuRebuild: false 223 | }, 224 | closeGridOnMouseOut: { 225 | fields: ['closeGridOnMouseOut'], 226 | requiresMenuRebuild: false 227 | }, 228 | offset: { 229 | fields: ['offsetX', 'offsetY'], 230 | requiresMenuRebuild: false 231 | }, 232 | disableAltClick: { 233 | fields: ['disableAltClick'], 234 | requiresMenuRebuild: false 235 | }, 236 | tabMode: { 237 | fields: ['tabMode', 'tabActive', 'lastTab', 'privateMode'], 238 | requiresMenuRebuild: false 239 | }, 240 | overwriteSearchEngines: { 241 | fields: ['overwriteSearchEngines'], 242 | requiresMenuRebuild: false 243 | }, 244 | multiMode: { 245 | fields: ['multiMode'], 246 | requiresMenuRebuild: false 247 | }, 248 | optionsMenuLocation: { 249 | fields: ['optionsMenuLocation'], 250 | requiresMenuRebuild: true 251 | }, 252 | siteSearch: { 253 | fields: ['siteSearch', 'siteSearchUrl'], 254 | requiresMenuRebuild: true 255 | }, 256 | resetOptions: { 257 | fields: ['forceSearchEnginesReload', 'resetPreferences', 'forceFaviconsReload'], 258 | requiresMenuRebuild: false, 259 | customReturn: 'updatedResetOptions' 260 | } 261 | }; -------------------------------------------------------------------------------- /scripts/history.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | 4 | document.addEventListener('DOMContentLoaded', getHistoryItems); 5 | 6 | async function getHistoryItems() { 7 | const historyItems = (await browser.storage.local.get('historyItems')).historyItems; 8 | const searchTerms = (await browser.storage.local.get('searchTerms')).searchTerms; 9 | await browser.storage.local.remove(['historyItems', 'searchTerms']); 10 | const ol = document.getElementById('historyItems'); 11 | const h2 = document.getElementById('title'); 12 | if (searchTerms) { 13 | h2.textContent = "History search results for " + searchTerms; 14 | } else { 15 | h2.textContent = "All history"; 16 | } 17 | console.log(historyItems); 18 | console.log(searchTerms); 19 | for (let item of historyItems) { 20 | const lvt = "Last Visit Time: " + new Date(item.lastVisitTime); 21 | let li = document.createElement('li'); 22 | li.style.margin = '0px'; 23 | li.style.padding = '0px'; 24 | let title = document.createElement('h3'); 25 | title.textContent = item.title; 26 | let url = document.createElement('p'); 27 | url.textContent = item.url; 28 | let lastVisitTime = document.createElement('p'); 29 | lastVisitTime.textContent = lvt; 30 | li.appendChild(title); 31 | li.appendChild(url); 32 | li.appendChild(lastVisitTime); 33 | ol.appendChild(li); 34 | } 35 | } -------------------------------------------------------------------------------- /scripts/hosts.js: -------------------------------------------------------------------------------- 1 | export const mycroftUrl = 'https://mycroftproject.com/installos.php/'; 2 | export const bingUrl = 'https://www.bing.com/visualsearch'; 3 | export const ddgUrl = 'https://icons.duckduckgo.com/ip3/'; 4 | export const horseIconUrl = 'https://icon.horse/icon/'; 5 | export const googleReverseImageSearchUrl = 'https://www.google.com/searchbyimage?sbisrc=1&safe=off&image_url='; 6 | export const googleLensUrl = 'https://lens.google.com/uploadbyurl?url='; 7 | export const tineyeUrl = 'https://www.tineye.com'; 8 | export const chatGPTUrl = 'https://chatgpt.com'; 9 | export const googleAIStudioUrl = 'https://aistudio.google.com/app/prompts/new_chat'; 10 | export const grokUrl = 'https://grok.com'; 11 | export const perplexityAIUrl = 'https://www.perplexity.ai'; 12 | export const poeUrl = 'https://poe.com'; 13 | export const claudeUrl = 'https://claude.ai'; 14 | export const youUrl = 'https://you.com'; 15 | export const andiUrl = 'https://andisearch.com'; 16 | export const aiUrls = [chatGPTUrl, googleAIStudioUrl, grokUrl, perplexityAIUrl, poeUrl, claudeUrl, youUrl, andiUrl]; -------------------------------------------------------------------------------- /scripts/popup.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | 4 | // Import constants to use STORAGE_KEYS 5 | import { STORAGE_KEYS } from './constants.js'; 6 | 7 | // Make the listener async to use await 8 | document.addEventListener('DOMContentLoaded', async () => { 9 | const aiEngines = ['chatgpt', 'gemini', 'grok', 'perplexity', 'poe', 'claude', 'you', 'andi']; 10 | const inputArea = document.getElementById('inputArea'); 11 | const outputArea = document.getElementById('outputArea'); 12 | let tagStyled = false; 13 | 14 | // --- Load logToConsole from storage --- 15 | let logToConsole = false; // Default value 16 | try { 17 | // Fetch using the correct key directly 18 | const data = await browser.storage.local.get(STORAGE_KEYS.LOG_TO_CONSOLE); 19 | // Check if the key exists and is a boolean 20 | if (typeof data[STORAGE_KEYS.LOG_TO_CONSOLE] === 'boolean') { 21 | logToConsole = data[STORAGE_KEYS.LOG_TO_CONSOLE]; 22 | } 23 | } catch (error) { 24 | console.error("Error loading logToConsole setting from storage:", error); 25 | // Keep the default value if loading fails 26 | } 27 | // -------------------------------------- 28 | 29 | // Focus the textarea when the popup opens 30 | inputArea.focus(); 31 | 32 | inputArea.addEventListener('keyup', (event) => { 33 | if (event.key === ' ') { 34 | const words = inputArea.value.trim().split(/\s+/); 35 | const firstWord = words[0].toLowerCase(); 36 | 37 | if (aiEngines.includes(firstWord) && !tagStyled) { 38 | styleAsTag(firstWord); 39 | } 40 | } else if (event.key === 'Backspace' && tagStyled && inputArea.selectionStart === 0) { 41 | event.preventDefault(); 42 | unstyleTag(); 43 | } else if (event.key === 'Enter') { 44 | event.preventDefault(); 45 | executeAISearch(); 46 | } else if (event.key === 'Escape') { 47 | window.close(); 48 | } 49 | }); 50 | 51 | function styleAsTag(firstWord) { 52 | const buttonSpan = document.createElement('span'); 53 | buttonSpan.className = 'button'; 54 | buttonSpan.textContent = firstWord; 55 | outputArea.appendChild(buttonSpan); 56 | inputArea.value = inputArea.value.replace(firstWord, '').trim(); 57 | inputArea.focus(); 58 | tagStyled = true; 59 | } 60 | 61 | function getAIEngine() { 62 | const outputHTML = outputArea.innerHTML.trim(); 63 | return outputHTML.replace(//g, '').replace(/<\/span>/g, ''); 64 | } 65 | 66 | function unstyleTag() { 67 | const aiEngine = getAIEngine(); 68 | if (aiEngines.includes(aiEngine)) { 69 | outputArea.innerHTML = ''; 70 | } 71 | inputArea.value = aiEngine + inputArea.value.trim(); 72 | tagStyled = false; 73 | } 74 | 75 | async function executeAISearch() { 76 | const aiEngine = getAIEngine(); 77 | const prompt = inputArea.value.trim(); 78 | if (tagStyled && aiEngine && prompt) { 79 | // Prepare the message data 80 | const messagePayload = { 81 | action: 'executeAISearch', 82 | data: { aiEngine, prompt } 83 | }; 84 | 85 | try { 86 | // 1. Send the message and wait for acknowledgment 87 | await browser.runtime.sendMessage(messagePayload); 88 | 89 | // 2. Close the window *after* the message has been acknowledged 90 | window.close(); 91 | 92 | } catch (error) { 93 | // Use logToConsole value loaded from storage 94 | if (logToConsole) console.error(`Error sending/handling executeAISearch message: ${error.message}`); 95 | // Optionally, inform the user via an alert or keep the popup open 96 | // alert(`Error: ${error.message}`); 97 | } 98 | } 99 | } 100 | }); 101 | -------------------------------------------------------------------------------- /scripts/subscription_choice.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | import ExtPay from '/libs/ExtPay.js'; 4 | import { DEBUG } from './constants.js'; 5 | 6 | const logToConsole = DEBUG; 7 | const extpay = ExtPay('context-search'); 8 | 9 | document.getElementById('trialBtn').addEventListener('click', () => { 10 | extpay.openTrialPage('7-day'); 11 | setTimeout(() => window.close(), 100); 12 | }); 13 | 14 | document.getElementById('subBtn').addEventListener('click', () => { 15 | extpay.openPaymentPage().then(() => window.close()); 16 | }); 17 | 18 | function i18n() { 19 | translateContent('data-i18n', 'textContent'); 20 | translateContent('data-i18n-placeholder', 'placeholder'); 21 | translateContent('data-i18n-title', 'title'); 22 | } 23 | 24 | // Translate content based on data attributes 25 | function translateContent(attribute, type) { 26 | let i18nElements = document.querySelectorAll('[' + attribute + ']'); 27 | 28 | if (logToConsole) console.log(`Translating ${i18nElements.length} elements`); 29 | if (logToConsole) console.log('Translating:', i18nElements); 30 | 31 | i18nElements.forEach(i => { 32 | const i18n_attrib = i.getAttribute(attribute); // Get key before try block 33 | try { 34 | const message = browser.i18n.getMessage(i18n_attrib); // Call getMessage 35 | 36 | if (logToConsole) console.log(`Translating key: "${i18n_attrib}" used by element:`, i, 'Message:', message); 37 | 38 | // Check if the message is empty or same as the key (indicates missing translation) 39 | if (!message || message === i18n_attrib) { 40 | if (logToConsole) console.warn(`Translation missing for key: "${i18n_attrib}" used by element:`, i); 41 | // Optionally, leave the original text/placeholder/title instead of setting empty string 42 | return; // Skip applying the empty/missing translation 43 | } 44 | 45 | switch (type) { 46 | case 'textContent': 47 | i.textContent = message; 48 | break; 49 | case 'placeholder': 50 | i.placeholder = message; 51 | break; 52 | case 'title': 53 | i.title = message; 54 | break; 55 | default: 56 | break; 57 | } 58 | } catch (ex) { // Catch errors during getMessage itself 59 | if (logToConsole) console.error(`Error getting translation for key "${i18n_attrib}":`, ex, "Element:", i); 60 | } 61 | }); 62 | } 63 | 64 | document.addEventListener('DOMContentLoaded', () => { 65 | i18n(); 66 | }); -------------------------------------------------------------------------------- /scripts/subscription_status.js: -------------------------------------------------------------------------------- 1 | /// Import browser polyfill for compatibility with Chrome and other browsers 2 | import '/libs/browser-polyfill.min.js'; 3 | import ExtPay from '/libs/ExtPay.js'; 4 | import { DEBUG, trialActive, daysRemaining, noTrialStarted, startTrial, trialExpired, subscriptionActive, subscriptionInactive, pay } from './constants.js'; 5 | 6 | const logToConsole = DEBUG; 7 | 8 | const extpay = ExtPay('context-search'); 9 | const statusDiv = document.getElementById('status'); 10 | 11 | async function renderStatus() { 12 | // Clear previous content 13 | statusDiv.innerHTML = ''; 14 | 15 | const user = await extpay.getUser(); 16 | if (logToConsole) console.log('User status:', user); // Log user object 17 | const now = new Date(); 18 | const sevenDaysMs = 1000 * 60 * 60 * 24 * 7; 19 | let trialDaysRemaining = 0; 20 | 21 | if (user.trialStartedAt) { 22 | const elapsed = now - user.trialStartedAt; 23 | trialDaysRemaining = Math.max(0, Math.ceil((sevenDaysMs - elapsed) / (1000 * 60 * 60 * 24))); 24 | } 25 | 26 | // Display statuses 27 | if (trialDaysRemaining > 0) { 28 | const p = document.createElement('p'); 29 | p.textContent = `${trialActive} ${trialDaysRemaining} ${daysRemaining}`; 30 | statusDiv.appendChild(p); 31 | } else if (!user.paid && !user.trialStartedAt) { 32 | const p = document.createElement('p'); 33 | p.textContent = `${noTrialStarted}`; 34 | statusDiv.appendChild(p); 35 | 36 | const trialBtn = document.createElement('button'); 37 | trialBtn.textContent = `${startTrial}`; 38 | trialBtn.addEventListener('click', () => { 39 | if (logToConsole) console.log('Attempting to open trial page via background script...'); // Log before call 40 | // Ask the background script to open the trial page 41 | browser.runtime.sendMessage({ action: "openTrialPage" }); 42 | // Close the popup after sending the message 43 | setTimeout(() => window.close(), 100); 44 | }); 45 | statusDiv.appendChild(trialBtn); 46 | } else if (trialDaysRemaining <= 0 && user.trialStartedAt) { 47 | const p = document.createElement('p'); 48 | p.textContent = `${trialExpired}`; 49 | statusDiv.appendChild(p); 50 | } 51 | 52 | // Paid status 53 | if (user.paid) { 54 | const p = document.createElement('p'); 55 | p.textContent = `${subscriptionActive}`; 56 | statusDiv.appendChild(p); 57 | } else { 58 | const p = document.createElement('p'); 59 | p.textContent = `${subscriptionInactive}`; 60 | statusDiv.appendChild(p); 61 | 62 | const payBtn = document.createElement('button'); 63 | payBtn.textContent = `${pay}`; 64 | payBtn.addEventListener('click', () => { 65 | // Ask background script to open payment page 66 | browser.runtime.sendMessage({ action: "openPaymentPage" }); 67 | setTimeout(() => window.close(), 100); 68 | }); 69 | statusDiv.appendChild(payBtn); 70 | } 71 | } 72 | 73 | function i18n() { 74 | translateContent('data-i18n', 'textContent'); 75 | translateContent('data-i18n-placeholder', 'placeholder'); 76 | translateContent('data-i18n-title', 'title'); 77 | } 78 | 79 | // Translate content based on data attributes 80 | function translateContent(attribute, type) { 81 | let i18nElements = document.querySelectorAll('[' + attribute + ']'); 82 | 83 | if (logToConsole) console.log(`Translating ${i18nElements.length} elements`); 84 | if (logToConsole) console.log('Translating:', i18nElements); 85 | 86 | i18nElements.forEach(i => { 87 | const i18n_attrib = i.getAttribute(attribute); // Get key before try block 88 | try { 89 | const message = browser.i18n.getMessage(i18n_attrib); // Call getMessage 90 | 91 | if (logToConsole) console.log(`Translating key: "${i18n_attrib}" used by element:`, i, 'Message:', message); 92 | 93 | // Check if the message is empty or same as the key (indicates missing translation) 94 | if (!message || message === i18n_attrib) { 95 | if (logToConsole) console.warn(`Translation missing for key: "${i18n_attrib}" used by element:`, i); 96 | // Optionally, leave the original text/placeholder/title instead of setting empty string 97 | return; // Skip applying the empty/missing translation 98 | } 99 | 100 | switch (type) { 101 | case 'textContent': 102 | i.textContent = message; 103 | break; 104 | case 'placeholder': 105 | i.placeholder = message; 106 | break; 107 | case 'title': 108 | i.title = message; 109 | break; 110 | default: 111 | break; 112 | } 113 | } catch (ex) { // Catch errors during getMessage itself 114 | if (logToConsole) console.error(`Error getting translation for key "${i18n_attrib}":`, ex, "Element:", i); 115 | } 116 | }); 117 | } 118 | 119 | document.addEventListener('DOMContentLoaded', () => { 120 | renderStatus(); 121 | i18n(); 122 | }); 123 | -------------------------------------------------------------------------------- /scripts/toggle_theme.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | let s = !1, 3 | themeToggle, 4 | n; 5 | 6 | document.addEventListener("DOMContentLoaded", () => { 7 | setupThemeToggle(); 8 | const storedTheme = JSON.parse(localStorage.theme); 9 | const theme = storedTheme.theme === "dark"; 10 | 11 | const tooltip = document.getElementById("ThemeToggle--tooltip"); 12 | while (tooltip.firstChild) { 13 | tooltip.removeChild(tooltip.firstChild); 14 | } 15 | 16 | const textNode = document.createTextNode(`Set theme to ${theme ? "light" : "dark"} (\u21E7+D)`); 17 | tooltip.appendChild(textNode); 18 | }), (themeToggle = document.querySelector("#ThemeToggle")), (s = !themeToggle); 19 | 20 | function setTheme(isDarkMode) { 21 | const theme = isDarkMode ? "dark" : "light"; 22 | document.documentElement.setAttribute("theme", theme); 23 | document.documentElement.setAttribute("data-theme", theme); 24 | localStorage.theme = JSON.stringify({ theme }); 25 | if (themeToggle) { 26 | themeToggle.checked = isDarkMode; 27 | s = false; 28 | } else if (s) { 29 | setTimeout(setTheme, 1, isDarkMode); 30 | } 31 | } 32 | 33 | function toggleTheme(isDarkMode) { 34 | setTheme(isDarkMode); 35 | const tooltip = document.getElementById("ThemeToggle--tooltip"); 36 | while (tooltip.firstChild) { 37 | tooltip.removeChild(tooltip.firstChild); 38 | } 39 | 40 | const textNode = document.createTextNode(`Set theme to ${isDarkMode ? "light" : "dark"} (\u21E7+D)`); 41 | tooltip.appendChild(textNode); 42 | } 43 | 44 | function setupThemeToggle() { 45 | const themeToggle = document.querySelector("#ThemeToggle"); 46 | 47 | themeToggle.addEventListener("change", () => toggleTheme(themeToggle.checked)); 48 | 49 | document.addEventListener("keydown", (event) => { 50 | const isBodyElement = event.target === document.body; 51 | const isKeyPressD = event.key === "D" || event.key === "d"; 52 | const isShiftKey = event.shiftKey; 53 | 54 | if (isBodyElement && isKeyPressD && isShiftKey) { 55 | event.preventDefault(); 56 | toggleTheme(!themeToggle.checked); 57 | } 58 | }); 59 | } 60 | 61 | try { 62 | (n = window.matchMedia("(prefers-color-scheme:dark)")), (n.onchange = (e) => setTheme(e.matches)); 63 | } catch (error) { 64 | console.warn("Could not set up prefers-color-scheme listener:", error); 65 | } 66 | 67 | try { 68 | let e = localStorage.theme, 69 | s = e && JSON.parse(e); 70 | setTheme(s ? /dark/.test(s.theme) : !!(n && n.matches)); 71 | } catch (error) { 72 | console.warn("Could not load theme from localStorage:", error); 73 | } 74 | })(); 75 | -------------------------------------------------------------------------------- /styles/addSearchEngineForPostRequest.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Raleway', Helvetica, Arial, sans-serif; 3 | padding: 20px; 4 | } 5 | 6 | label { 7 | margin-bottom: 5px; 8 | display: block; 9 | } 10 | 11 | input { 12 | margin-bottom: 10px; 13 | width: 100%; 14 | padding: 5px; 15 | } 16 | 17 | .btn-container { 18 | display: flex; 19 | justify-content: flex-end; 20 | margin-top: 20px; 21 | } 22 | 23 | button { 24 | padding: 8px 15px; 25 | margin-left: 10px; 26 | } -------------------------------------------------------------------------------- /styles/addSearchEngineOrFolder.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | background: #0000cc; 4 | margin: 0; 5 | padding: 0; 6 | border: 0; 7 | width: 100%; 8 | height: 100%; 9 | overflow: hidden; 10 | } 11 | 12 | body { 13 | font-family: 'Raleway', Calibri, Helvetica, Arial, sans-serif; 14 | font-size: 10pt; 15 | line-height: 1.618em; 16 | } 17 | 18 | /* 19 | Style the tabs 20 | */ 21 | .wrapper { 22 | max-width: 75rem; 23 | width: 100%; 24 | margin: 0 auto; 25 | } 26 | 27 | .tabs { 28 | position: relative; 29 | margin: 0; 30 | background: #0000cc; 31 | height: 27.75rem; 32 | } 33 | 34 | .tabs::before, 35 | .tabs::after { 36 | content: ""; 37 | display: table; 38 | } 39 | 40 | .tabs::after { 41 | clear: both; 42 | } 43 | 44 | .tab { 45 | float: left; 46 | } 47 | 48 | .tab-switch { 49 | display: none; 50 | } 51 | 52 | .tab-label { 53 | position: relative; 54 | display: inline-block; 55 | line-height: 2.75em; 56 | height: 3em; 57 | padding: 0 1.618em; 58 | background: #0077ff; 59 | border-right: 0.125rem solid #fff; 60 | color: #fff; 61 | cursor: pointer; 62 | top: 0; 63 | transition: all 0.25s; 64 | } 65 | 66 | .tab-label:hover { 67 | top: -0.25rem; 68 | transition: top 0.25s; 69 | } 70 | 71 | .tab-content { 72 | height: 25rem; 73 | width: 100%; 74 | position: absolute; 75 | z-index: 1; 76 | top: 2.75em; 77 | left: 0; 78 | padding: 1.618rem; 79 | background: #fff; 80 | color: #2c3e50; 81 | opacity: 0; 82 | transition: all 0.35s; 83 | } 84 | 85 | .tab-switch:checked+.tab-label { 86 | background: #fff; 87 | color: #2c3e50; 88 | border-bottom: 0; 89 | border-right: 0.125rem solid #fff; 90 | transition: all 0.35s; 91 | z-index: 1; 92 | top: -0.0625rem; 93 | } 94 | 95 | .tab-switch:checked+label+.tab-content { 96 | z-index: 2; 97 | opacity: 1; 98 | transition: all 0.35s; 99 | } 100 | 101 | .option { 102 | margin-left: 3px; 103 | } 104 | 105 | .option label { 106 | color: #444; 107 | } 108 | 109 | .tab-label { 110 | background: #0077ff; 111 | border-right: 0.125rem solid #0000cc; 112 | color: #fff; 113 | } 114 | 115 | .tab-content { 116 | background: lightskyblue; 117 | color: #0000cc; 118 | } 119 | 120 | .tab-switch:checked+.tab-label { 121 | background: lightskyblue; 122 | color: #0000cc; 123 | border-right: 0.125rem solid #0000cc; 124 | } 125 | 126 | #addChatGPTPromptTab #ai-provider { 127 | margin-left: 10px; 128 | width: 140px; 129 | } 130 | 131 | #addChatGPTPromptTab #prompt { 132 | margin-top: 15px; 133 | margin-left: 35px; 134 | width: 400px; 135 | } 136 | 137 | #addSearchEngineTab #url { 138 | width: 400px; 139 | margin-left: 10px; 140 | } 141 | 142 | #addSearchEngineTab #name, 143 | #addChatGPTPromptTab #promptName { 144 | width: 195px; 145 | margin-left: 10px; 146 | } 147 | 148 | #addFolderTab #folderName { 149 | width: 195px; 150 | } 151 | 152 | #addSearchEngineTab #keyword, 153 | #addChatGPTPromptTab #promptKeyword, 154 | #addFolderTab #folderKeyword { 155 | width: 80px; 156 | margin-left: 10px; 157 | } 158 | 159 | #addSearchEngineTab #kb-shortcut, 160 | #addChatGPTPromptTab #prompt-kb-shortcut, 161 | #addFolderTab #folder-kb-shortcut { 162 | width: 130px; 163 | margin-left: 10px; 164 | } 165 | 166 | #multitab, 167 | #promptMultitab { 168 | margin-left: 10px; 169 | } 170 | 171 | .buttonrow { 172 | margin-left: 35px; 173 | margin-top: 15px; 174 | } 175 | 176 | #addSeparatorTab h3, 177 | #addFolderTab h3, 178 | #addSearchEngineTab h3, 179 | #addChatGPTPromptTab h3, 180 | #addFolderTab #folderName { 181 | margin-left: 35px; 182 | } -------------------------------------------------------------------------------- /styles/bookmark.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Raleway', Helvetica, Arial, sans-serif; 3 | margin: 10px; 4 | width: 680px; 5 | /* Adjust the width to be slightly less than the popup width */ 6 | } 7 | 8 | label { 9 | display: block; 10 | margin-bottom: 8px; 11 | font-weight: bold; 12 | } 13 | 14 | input[type="text"], 15 | input[type="url"], 16 | select { 17 | width: 100%; 18 | padding: 8px; 19 | margin-bottom: 15px; 20 | box-sizing: border-box; 21 | } 22 | 23 | #buttonContainer { 24 | display: flex; 25 | justify-content: space-between; 26 | } 27 | 28 | button { 29 | padding: 10px 20px; 30 | font-size: 14px; 31 | cursor: pointer; 32 | } 33 | 34 | #cancelButton { 35 | background-color: #f0f0f0; 36 | border: 1px solid #ccc; 37 | } 38 | 39 | #addBookmarkButton { 40 | background-color: #0078D7; 41 | border: none; 42 | color: white; 43 | } 44 | 45 | #addBookmarkButton:hover { 46 | background-color: #005A9E; 47 | } -------------------------------------------------------------------------------- /styles/bookmarkRemoval.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Raleway', Helvetica, Arial, sans-serif; 3 | display: flex; 4 | flex-direction: column; 5 | justify-content: center; 6 | align-items: center; 7 | height: 100vh; 8 | margin: 0; 9 | background-color: #f4f4f4; 10 | } 11 | 12 | p { 13 | margin-bottom: 20px; 14 | } 15 | 16 | .button-container { 17 | width: 100%; 18 | display: flex; 19 | justify-content: space-between; 20 | padding: 0 20px; 21 | } 22 | 23 | button { 24 | padding: 10px 20px; 25 | font-size: 16px; 26 | border: none; 27 | cursor: pointer; 28 | border-radius: 5px; 29 | font-family: 'Raleway', sans-serif; 30 | } 31 | 32 | #noBtn { 33 | margin-left: 20px; 34 | background-color: #f44336; 35 | /* Red */ 36 | color: white; 37 | } 38 | 39 | #yesBtn { 40 | margin-right: 20px; 41 | background-color: #4CAF50; 42 | /* Green */ 43 | color: white; 44 | } -------------------------------------------------------------------------------- /styles/options.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: 'Raleway', Calibri, Helvetica, Arial, sans-serif; 7 | font-size: 10pt; 8 | line-height: 1.618em; 9 | } 10 | 11 | fieldset { 12 | border-radius: 5px; 13 | border: 1px solid gray; 14 | } 15 | 16 | .left fieldset { 17 | display: inline-block; 18 | width: 480px; 19 | margin-right: 10px; 20 | } 21 | 22 | .right fieldset { 23 | display: inline-block; 24 | } 25 | 26 | span.option { 27 | display: inline-block; 28 | width: 150px; 29 | padding-bottom: 5px; 30 | } 31 | 32 | div#tabMode, 33 | div#multiMode { 34 | color-scheme: light; 35 | max-width: 320px; 36 | background-color: lightgray; 37 | color: darkslategray; 38 | } 39 | 40 | .icon { 41 | font-size: 1.3em; 42 | } 43 | 44 | div.nav { 45 | float: right; 46 | } 47 | 48 | input[type="checkbox"] { 49 | display: inline-block; 50 | width: 15px; 51 | margin-right: 5px; 52 | } 53 | 54 | input[type="number"] { 55 | display: inline-block; 56 | width: 60px; 57 | margin-bottom: 5px; 58 | } 59 | 60 | .search-engine { 61 | background-color: lightskyblue; 62 | padding: 5px; 63 | margin-left: 30px; 64 | } 65 | 66 | .folder { 67 | padding: 1px; 68 | margin-left: 30px; 69 | } 70 | 71 | .ion-folder { 72 | font-size: 1.5em; 73 | margin-right: 10px; 74 | } 75 | 76 | .cell label, 77 | .cell input { 78 | display: inline-block; 79 | width: 45%; 80 | } 81 | 82 | #searchEngines div { 83 | /* Set the default cursor style */ 84 | cursor: default; 85 | 86 | /* Hide the dotted border by default */ 87 | border: none; 88 | } 89 | 90 | html[theme=light] #searchEngines div.search-engine:hover, 91 | html[theme=dark] #searchEngines div.search-engine:hover { 92 | /* Show the dotted border when hovered */ 93 | border: 3px dotted blue; 94 | 95 | /* Change the cursor to a hand when hovered */ 96 | cursor: pointer; 97 | } 98 | 99 | html[theme=light] #searchEngines div.folder:hover, 100 | html[theme=dark] #searchEngines div.folder:hover { 101 | /* Show the dotted border when hovered */ 102 | border: 3px dotted rgb(240, 177, 249); 103 | 104 | /* Change the cursor to a hand when hovered */ 105 | cursor: pointer; 106 | } 107 | 108 | #searchEngines div textarea { 109 | margin: 0px; 110 | padding: 5px; 111 | } 112 | 113 | button.remove { 114 | text-align: center; 115 | } 116 | 117 | div, 118 | .va { 119 | vertical-align: top; 120 | } 121 | 122 | textarea { 123 | vertical-align: top; 124 | } 125 | 126 | div input[type="checkbox"] { 127 | margin-left: 5px; 128 | } 129 | 130 | div img { 131 | vertical-align: middle; 132 | width: 20px; 133 | height: 20px; 134 | margin-left: 5px; 135 | margin-right: 5px; 136 | margin-bottom: 0px; 137 | padding-bottom: 0px; 138 | } 139 | 140 | #container { 141 | margin-right: 25px; 142 | } 143 | 144 | #container label, 145 | #container #name { 146 | display: inline-block; 147 | width: 210px; 148 | } 149 | 150 | #addSearchEngineTab #name, 151 | #addChatGPTPromptTab #promptName { 152 | display: inline-block; 153 | width: 195px; 154 | } 155 | 156 | .left input[type="radio"]+label { 157 | width: 120px; 158 | } 159 | 160 | input#gridMode+label, 161 | input#optionsMenuAtTop+label { 162 | width: 600px; 163 | } 164 | 165 | #container input[type="url"], 166 | #addSearchEngineTab #url, 167 | #addChatGPTPromptTab #prompt { 168 | display: inline-block; 169 | width: 400px; 170 | margin-left: 10px; 171 | } 172 | 173 | #container .kb-shortcut, 174 | #addSearchEngineTab #kb-shortcut, 175 | #addChatGPTPromptTab #prompt-kb-shortcut { 176 | width: 130px; 177 | margin-left: 10px; 178 | } 179 | 180 | #container .keyword, 181 | #addSearchEngineTab #keyword, 182 | #addChatGPTPromptTab #promptKeyword { 183 | width: 80px; 184 | margin-left: 10px; 185 | } 186 | 187 | h2 { 188 | margin: 20px 0px 10px 0px; 189 | font-size: 14pt; 190 | font-weight: bold; 191 | text-align: center; 192 | } 193 | 194 | #container button { 195 | margin-left: 10px; 196 | } 197 | 198 | #container div { 199 | margin-bottom: 5px; 200 | } 201 | 202 | #upl { 203 | margin-top: 10px; 204 | margin-bottom: 8px; 205 | } 206 | 207 | h4 { 208 | margin-left: 0; 209 | margin-right: 0; 210 | margin-top: 5px; 211 | margin-bottom: 3px; 212 | } 213 | 214 | p, 215 | .right button, 216 | select { 217 | margin-top: 5px; 218 | margin-bottom: 5px; 219 | } 220 | 221 | select { 222 | margin-right: 5px; 223 | } 224 | 225 | #flexbox-container { 226 | margin-left: 10px; 227 | } 228 | 229 | #flexbox-container>div.left>div.preferences { 230 | display: -ms-flex; 231 | display: -webkit-flex; 232 | display: flex; 233 | flex-wrap: wrap; 234 | flex-direction: row; 235 | align-content: flex-start; 236 | margin-right: 10px; 237 | } 238 | 239 | #flexbox-container>div.right, 240 | #flexbox-container>div.left { 241 | padding: 10px; 242 | } 243 | 244 | #container hr { 245 | display: inline-block; 246 | height: 5px; 247 | width: 900px; 248 | margin-left: 6px; 249 | border-width: 0; 250 | background-color: #005588; 251 | } 252 | 253 | hr.ruler { 254 | opacity: 0.5; 255 | margin: 1em 0.5em; 256 | } 257 | 258 | @media screen and (min-width: 1520px) { 259 | #flexbox-container { 260 | display: -ms-flex; 261 | display: -webkit-flex; 262 | display: flex; 263 | } 264 | 265 | #flexbox-container .left { 266 | width: 30%; 267 | } 268 | 269 | #flexbox-container .right { 270 | width: 70%; 271 | } 272 | 273 | #flexbox-container>div.left { 274 | margin-right: 10px; 275 | } 276 | } 277 | 278 | .buttonrow button { 279 | margin-top: 12px; 280 | margin-bottom: 4px; 281 | } 282 | 283 | .sort { 284 | margin-left: 10px; 285 | color: black; 286 | } 287 | 288 | .ms { 289 | max-width: 650px; 290 | } 291 | 292 | code { 293 | background: #eaeaea; 294 | padding: 0px 5px; 295 | border-radius: 2px; 296 | } 297 | 298 | .p-sm { 299 | padding: 0.5em; 300 | } 301 | 302 | .subfolder { 303 | margin-left: 25px; 304 | } 305 | 306 | .empty { 307 | width: 100%; 308 | height: 20px; 309 | margin: 5px 0px; 310 | border: 2px dashed black; 311 | } 312 | 313 | /* 314 | Slider bubble styling 315 | */ 316 | .slidercontainer { 317 | display: grid; 318 | grid-template-columns: 170px auto; 319 | position: relative; 320 | margin: 0 auto 2rem; 321 | } 322 | 323 | .slidercontainer>label { 324 | grid-column: 1 / 2; 325 | } 326 | 327 | .slider { 328 | grid-column: 2 / span 1; 329 | } 330 | 331 | .bubble { 332 | grid-column: 2 / span 1; 333 | margin-top: 22px; 334 | background: red; 335 | color: white; 336 | padding: 4px 8px; 337 | position: absolute; 338 | border-radius: 5px; 339 | left: 50%; 340 | transform: translateX(-50%); 341 | } 342 | 343 | .bubble::after { 344 | grid-column: 2 / span 1; 345 | margin-top: 22px; 346 | content: ""; 347 | position: absolute; 348 | top: 0; 349 | left: 50%; 350 | width: 0; 351 | height: 0; 352 | border: 5px solid transparent; 353 | border-bottom-color: red; 354 | border-top: 0; 355 | margin-left: -5px; 356 | margin-top: -5px; 357 | } 358 | 359 | /* 360 | Style the tabs 361 | */ 362 | .wrapper { 363 | max-width: 75rem; 364 | width: 100%; 365 | margin: 0 auto; 366 | } 367 | 368 | .tabs { 369 | position: relative; 370 | margin: 1rem 0; 371 | background: #0000cc; 372 | height: 27.75rem; 373 | } 374 | 375 | .tabs::before, 376 | .tabs::after { 377 | content: ""; 378 | display: table; 379 | } 380 | 381 | .tabs::after { 382 | clear: both; 383 | } 384 | 385 | .tab { 386 | float: left; 387 | } 388 | 389 | .tab-switch { 390 | display: none; 391 | } 392 | 393 | .tab-label { 394 | position: relative; 395 | display: inline-block; 396 | line-height: 2.75em; 397 | height: 3em; 398 | padding: 0 1.618em; 399 | background: #0077ff; 400 | border-right: 0.125rem solid #fff; 401 | color: #fff; 402 | cursor: pointer; 403 | top: 0; 404 | transition: all 0.25s; 405 | } 406 | 407 | .tab-label:hover { 408 | top: -0.25rem; 409 | transition: top 0.25s; 410 | } 411 | 412 | .tab-content { 413 | height: 25rem; 414 | min-width: 55rem; 415 | position: absolute; 416 | z-index: 1; 417 | top: 2.75em; 418 | left: 0; 419 | padding: 1.618rem; 420 | background: #fff; 421 | color: #2c3e50; 422 | opacity: 0; 423 | transition: all 0.35s; 424 | } 425 | 426 | .tab-switch:checked+.tab-label { 427 | background: #fff; 428 | color: #2c3e50; 429 | border-bottom: 0; 430 | border-right: 0.125rem solid #fff; 431 | transition: all 0.35s; 432 | z-index: 1; 433 | top: -0.0625rem; 434 | } 435 | 436 | .tab-switch:checked+label+.tab-content { 437 | z-index: 2; 438 | opacity: 1; 439 | transition: all 0.35s; 440 | } 441 | 442 | .option { 443 | margin-left: 3px; 444 | } 445 | 446 | html[theme=dark] .option label { 447 | color: #444; 448 | } 449 | 450 | html[theme=dark] .tab-label { 451 | background: #0077ff; 452 | border-right: 0.125rem solid #0000cc; 453 | color: #fff; 454 | } 455 | 456 | html[theme=dark] .tab-content { 457 | background: lightskyblue; 458 | color: #0000cc; 459 | } 460 | 461 | html[theme=dark] .tab-switch:checked+.tab-label { 462 | background: lightskyblue; 463 | color: #0000cc; 464 | border-right: 0.125rem solid #0000cc; 465 | } 466 | 467 | /* Loading and error states */ 468 | .loading { 469 | padding: 20px; 470 | text-align: center; 471 | color: #666; 472 | font-style: italic; 473 | background: #f5f5f5; 474 | border-radius: 4px; 475 | margin: 10px 0; 476 | } 477 | 478 | .loading::after { 479 | content: '...'; 480 | animation: dots 1.5s steps(5, end) infinite; 481 | } 482 | 483 | @keyframes dots { 484 | 485 | 0%, 486 | 20% { 487 | content: '.'; 488 | } 489 | 490 | 40% { 491 | content: '..'; 492 | } 493 | 494 | 60% { 495 | content: '...'; 496 | } 497 | 498 | 80%, 499 | 100% { 500 | content: ''; 501 | } 502 | } 503 | 504 | .error { 505 | padding: 20px; 506 | text-align: center; 507 | color: #721c24; 508 | background-color: #f8d7da; 509 | border: 1px solid #f5c6cb; 510 | border-radius: 4px; 511 | margin: 10px 0; 512 | } -------------------------------------------------------------------------------- /styles/popup.css: -------------------------------------------------------------------------------- 1 | /* General styling for popup container */ 2 | body { 3 | color: #ccc; 4 | background-color: #333; 5 | font-family: 'Raleway', Helvetica, Arial, sans-serif; 6 | } 7 | 8 | p { 9 | color: #ccc; 10 | padding: 5px; 11 | font-size: 11px; 12 | } 13 | 14 | /* Styling the button */ 15 | .button { 16 | background-color: #03d; 17 | border: none; 18 | color: #ddd; 19 | padding: 5px 15px; 20 | text-align: center; 21 | text-decoration: none; 22 | display: inline-block; 23 | margin-right: 5px; 24 | border-radius: 20px; 25 | font-size: 14px; 26 | line-height: 1; 27 | vertical-align: baseline; 28 | } 29 | 30 | .button:hover { 31 | color: black; 32 | background-color: #ddd; 33 | transition-duration: 0.5s; 34 | } 35 | 36 | #container { 37 | display: flex; 38 | align-items: baseline; 39 | width: 100%; 40 | height: 35px; 41 | /* Fill the remaining height */ 42 | } 43 | 44 | #inputArea { 45 | flex: 1; 46 | font-size: 14px; 47 | padding: 5px; 48 | box-sizing: border-box; 49 | border: none; 50 | resize: none; 51 | outline: none; 52 | overflow: hidden; 53 | line-height: 1.5; 54 | color: #ccc; 55 | background-color: #333; 56 | } 57 | 58 | #outputArea { 59 | display: flex; 60 | align-items: center; 61 | margin-right: 5px; 62 | color: #ccc; 63 | background-color: #333; 64 | } -------------------------------------------------------------------------------- /styles/sidebar.css: -------------------------------------------------------------------------------- 1 | /* Mobile-friendly styles for search results in sidebar */ 2 | :root { 3 | --sidebar-width: 100%; 4 | --sidebar-height: 100vh; 5 | } 6 | 7 | /* Force viewport for mobile-friendly display */ 8 | @viewport { 9 | width: device-width; 10 | zoom: 1.0; 11 | } 12 | 13 | /* Basic reset for mobile view */ 14 | html { 15 | width: 100% !important; 16 | overflow-x: hidden !important; 17 | } 18 | 19 | body { 20 | margin: 0 !important; 21 | padding: 0 !important; 22 | width: 100% !important; 23 | max-width: 100% !important; 24 | overflow-x: hidden !important; 25 | -webkit-text-size-adjust: 100%; 26 | } 27 | 28 | /* Force all elements to respect container width */ 29 | *, *::before, *::after { 30 | max-width: 100% !important; 31 | box-sizing: border-box !important; 32 | overflow-wrap: break-word !important; 33 | word-wrap: break-word !important; 34 | } 35 | 36 | /* Responsive images and media */ 37 | img, video, iframe { 38 | max-width: 100% !important; 39 | height: auto !important; 40 | display: block !important; 41 | } 42 | 43 | /* Adjust text size and wrapping for readability */ 44 | body, p, div, span, a { 45 | font-size: 16px !important; 46 | line-height: 1.5 !important; 47 | white-space: normal !important; 48 | word-break: break-word !important; 49 | } 50 | 51 | /* Search results container styles */ 52 | #main, #b_content, #center_col, .content-area, #rso { 53 | width: 100% !important; 54 | max-width: 100% !important; 55 | margin: 0 !important; 56 | padding: 10px !important; 57 | overflow-x: hidden !important; 58 | } 59 | 60 | /* Individual search result styles */ 61 | .g, /* Google */ 62 | .b_algo, /* Bing */ 63 | .result, /* Generic */ 64 | .srg, /* Google */ 65 | .bm_details_overlay, /* Bing */ 66 | .b_widePag { /* Bing */ 67 | width: 100% !important; 68 | max-width: 100% !important; 69 | margin: 10px 0 !important; 70 | padding: 10px !important; 71 | overflow-x: hidden !important; 72 | } 73 | 74 | /* Hide unnecessary elements that might break layout */ 75 | header nav, 76 | #header, 77 | #navbar, 78 | #footcnt, 79 | #footer, 80 | .related-searches, 81 | aside, 82 | #b_header, /* Bing header */ 83 | #b_footer, /* Bing footer */ 84 | #b_context, /* Bing sidebar */ 85 | #rhs, /* Google sidebar */ 86 | .commercial /* Ads */ { 87 | display: none !important; 88 | } 89 | 90 | /* Links and interactive elements */ 91 | a { 92 | padding: 8px 0 !important; 93 | display: inline-block !important; 94 | max-width: 100% !important; 95 | overflow-wrap: break-word !important; 96 | } 97 | 98 | /* Tables */ 99 | table, thead, tbody, tr, td, th { 100 | max-width: 100% !important; 101 | display: block !important; 102 | word-break: break-word !important; 103 | } 104 | 105 | /* Force single column layout */ 106 | div[role="main"], 107 | div[role="complementary"], 108 | div[role="navigation"] { 109 | float: none !important; 110 | width: 100% !important; 111 | margin: 0 !important; 112 | padding: 0 !important; 113 | } 114 | 115 | /* Additional Bing-specific fixes */ 116 | .b_searchbox, 117 | .b_searchboxForm, 118 | #b_results > li { 119 | max-width: 100% !important; 120 | overflow-x: hidden !important; 121 | } 122 | 123 | /* Additional Google-specific fixes */ 124 | #search, 125 | .srg, 126 | .g, 127 | .rc { 128 | width: 100% !important; 129 | margin: 0 !important; 130 | padding: 10px !important; 131 | } --------------------------------------------------------------------------------