diff --git a/README.md b/README.md index 29693ee..f614d23 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Introducing Noi: an AI-enhanced, customizable browser designed to streamline your digital experience: -**AI Support: [ChatGPT](https://chatgpt.com), [Claude](https://claude.ai), [Gemini](https://gemini.google.com), [DeepSeek](https://chat.deepseek.com), [GitHub Copilot](https://github.com/copilot), [HuggingChat](https://huggingface.co/chat), and more.** +**AI Support: [ChatGPT](https://chatgpt.com), [Claude](https://claude.ai), [Gemini](https://gemini.google.com), [Grok](https://grok.com), [DeepSeek](https://chat.deepseek.com), [GitHub Copilot](https://github.com/copilot), [HuggingChat](https://huggingface.co/chat), and more.** - **Browser**: Noi not only includes curated AI websites but also allows the addition of any URL, providing a tailored browsing experience ([Noi Configs](./configs)). - **Prompts Management**: Offers robust customization options including the addition, synchronization, batch tagging, and removal of prompts. @@ -110,9 +110,8 @@ Learn more: [electronjs/doc](https://www.electronjs.org/docs/latest/api/extensio | Name | Version | Description | | --- | --- | --- | -| [@noi/ask](https://github.com/lencx/Noi/tree/main/extensions/noi-ask) | 0.1.17 | The best assistant for batch asking and quick typing of prompts. | +| [@noi/ask](https://github.com/lencx/Noi/tree/main/extensions/noi-ask) | 0.1.18 | The best assistant for batch asking and quick typing of prompts. | | [@noi/ask-custom](https://github.com/lencx/Noi/tree/main/extensions/noi-ask-custom) | 0.1.0 | The best assistant for batch asking and quick typing of prompts. | -| [@noi/export-chatgpt](https://github.com/lencx/Noi/tree/main/extensions/noi-export-chatgpt) | 0.1.1 | ChatGPT chat history export, supports PDF, Image, and Markdown formats. | | [@noi/reset](https://github.com/lencx/Noi/tree/main/extensions/noi-reset) | 0.1.3 | Reset certain website styles to enhance compatibility with Noi. | diff --git a/configs/noi.mode.cn.json b/configs/noi.mode.cn.json index 37099d0..b39704a 100644 --- a/configs/noi.mode.cn.json +++ b/configs/noi.mode.cn.json @@ -51,6 +51,12 @@ "text": "Gemini", "url": "https://gemini.google.com" }, + { + "id": "noi:grok", + "parent": "noi@ai", + "text": "Grok", + "url": "https://grok.com" + }, { "id": "noi:deepseek", "parent": "noi@ai", diff --git a/configs/noi.mode.json b/configs/noi.mode.json index 4963163..0120ada 100644 --- a/configs/noi.mode.json +++ b/configs/noi.mode.json @@ -57,6 +57,12 @@ "text": "Gemini", "url": "https://gemini.google.com" }, + { + "id": "noi:grok", + "parent": "noi@ai", + "text": "Grok", + "url": "https://grok.com" + }, { "id": "noi:deepseek", "parent": "noi@ai", diff --git a/extensions/README.md b/extensions/README.md index 9f2bc99..4b21d8a 100644 --- a/extensions/README.md +++ b/extensions/README.md @@ -9,8 +9,7 @@ Learn more: [electronjs/doc](https://www.electronjs.org/docs/latest/api/extensio | Name | Version | Description | | --- | --- | --- | -| [@noi/ask](https://github.com/lencx/Noi/tree/main/extensions/noi-ask) | 0.1.17 | The best assistant for batch asking and quick typing of prompts. | +| [@noi/ask](https://github.com/lencx/Noi/tree/main/extensions/noi-ask) | 0.1.18 | The best assistant for batch asking and quick typing of prompts. | | [@noi/ask-custom](https://github.com/lencx/Noi/tree/main/extensions/noi-ask-custom) | 0.1.0 | The best assistant for batch asking and quick typing of prompts. | -| [@noi/export-chatgpt](https://github.com/lencx/Noi/tree/main/extensions/noi-export-chatgpt) | 0.1.1 | ChatGPT chat history export, supports PDF, Image, and Markdown formats. | | [@noi/reset](https://github.com/lencx/Noi/tree/main/extensions/noi-reset) | 0.1.3 | Reset certain website styles to enhance compatibility with Noi. | \ No newline at end of file diff --git a/extensions/noi-ask/main.js b/extensions/noi-ask/main.js index 5f25518..274a1ce 100644 --- a/extensions/noi-ask/main.js +++ b/extensions/noi-ask/main.js @@ -70,24 +70,13 @@ class OpenAIAsk extends NoiAsk { } } -class SoraAsk extends NoiAsk { - static name = 'Sora'; - static url = 'https://sora.com'; +class GrokAsk extends NoiAsk { + static name = 'Grok'; + static url = 'https://grok.com'; static submit() { - const buttons = document.querySelectorAll('.surface-composer button'); - const lastButton = buttons[buttons.length - 1]; - if (lastButton) this.autoClick(lastButton); - } -} - -class PoeAsk extends NoiAsk { - static name = 'Poe'; - static url = 'https://poe.com'; - - static submit() { - const btn = document.querySelectorAll('button[class*="ChatMessageSendButton_sendButton"]')[0]; - if (btn) this.autoClick(btn); + const btn = document.querySelector('button[type="submit"]'); + if (btn) btn.click(); } } @@ -179,6 +168,28 @@ class HuggingChatAsk extends NoiAsk { } } + +class SoraAsk extends NoiAsk { + static name = 'Sora'; + static url = 'https://sora.com'; + + static submit() { + const buttons = document.querySelectorAll('.surface-composer button'); + const lastButton = buttons[buttons.length - 1]; + if (lastButton) this.autoClick(lastButton); + } +} + +class PoeAsk extends NoiAsk { + static name = 'Poe'; + static url = 'https://poe.com'; + + static submit() { + const btn = document.querySelectorAll('button[class*="ChatMessageSendButton_sendButton"]')[0]; + if (btn) this.autoClick(btn); + } +} + class PerplexityAsk extends NoiAsk { static name = 'Perplexity'; static url = 'https://www.perplexity.ai'; @@ -243,6 +254,36 @@ class CopilotAsk extends NoiAsk { } } +class GitHubCopilotAsk extends NoiAsk { + static name = 'GitHub'; + static url = 'https://github.com/copilot'; + + static sync(message) { + const inputElement = document.querySelector('form #copilot-chat-textarea'); + if (inputElement) { + inputElement.focus(); + document.execCommand('undo'); + document.execCommand('insertText', false, message); + } + } + + static submit() { + const btns = document.querySelectorAll('form button'); + const btn = btns[btns.length - 1]; + if (btn) this.autoClick(btn); + } +} + +class NotebooklmAsk extends NoiAsk { + static name = 'NotebookLM'; + static url = 'https://notebooklm.google.com'; + + static submit() { + const btn = document.querySelector('form button[type="submit"]'); + if (btn) btn.click(); + } +} + class PiAsk extends NoiAsk { static name = 'Pi'; static url = 'https://pi.ai/talk'; @@ -286,6 +327,27 @@ class YouAsk extends NoiAsk { } } + +class GroqAsk extends NoiAsk { + static name = 'Groq'; + static url = 'https://groq.com'; + + static submit() { + const btn = document.querySelector('form button[type="submit"]'); + if (btn) btn.click(); + } +} + +class SunoAsk extends NoiAsk { + static name = 'Suno AI'; + static url = 'https://suno.com'; + + static submit() { + const btn = Array.from(document.querySelectorAll('button')).find(i => i.innerText.includes('Create')); + if (btn) btn.click(); + } +} + class CozeCNAsk extends NoiAsk { static name = 'Coze'; static url = 'https://www.coze.cn/home'; @@ -342,36 +404,6 @@ class TongyiAsk extends NoiAsk { } } -class GroqAsk extends NoiAsk { - static name = 'Groq'; - static url = 'https://groq.com'; - - static submit() { - const btn = document.querySelector('form button[type="submit"]'); - if (btn) btn.click(); - } -} - -class SunoAsk extends NoiAsk { - static name = 'Suno AI'; - static url = 'https://suno.com'; - - static submit() { - const btn = Array.from(document.querySelectorAll('button')).find(i => i.innerText.includes('Create')); - if (btn) btn.click(); - } -} - -class NotebooklmAsk extends NoiAsk { - static name = 'NotebookLM'; - static url = 'https://notebooklm.google.com'; - - static submit() { - const btn = document.querySelector('form button[type="submit"]'); - if (btn) btn.click(); - } -} - class JimengAsk extends NoiAsk { static name = 'Jimeng'; static url = 'https://jimeng.jianying.com'; @@ -401,26 +433,6 @@ class JimengAsk extends NoiAsk { } } -class GitHubCopilotAsk extends NoiAsk { - static name = 'GitHub'; - static url = 'https://github.com/copilot'; - - static sync(message) { - const inputElement = document.querySelector('form #copilot-chat-textarea'); - if (inputElement) { - inputElement.focus(); - document.execCommand('undo'); - document.execCommand('insertText', false, message); - } - } - - static submit() { - const btns = document.querySelectorAll('form button'); - const btn = btns[btns.length - 1]; - if (btn) this.autoClick(btn); - } -} - class MetasoAsk extends NoiAsk { static name = 'Metaso'; // 秘塔 static url = 'https://metaso.cn'; @@ -433,25 +445,26 @@ class MetasoAsk extends NoiAsk { window.NoiAsk = { OpenAIAsk, - SoraAsk, - PoeAsk, ClaudeAsk, GeminiAsk, + GrokAsk, + CopilotAsk, HuggingChatAsk, PerplexityAsk, - CopilotAsk, + NotebooklmAsk, + GitHubCopilotAsk, PiAsk, + GroqAsk, + PoeAsk, + SoraAsk, + SunoAsk, CozeAsk, YouAsk, CozeCNAsk, DoubaoAsk, ChatGLMAsk, TongyiAsk, - GroqAsk, - SunoAsk, - NotebooklmAsk, JimengAsk, DeepSeekAsk, - GitHubCopilotAsk, MetasoAsk, }; diff --git a/extensions/noi-ask/manifest.json b/extensions/noi-ask/manifest.json index 0835fd9..be8fff4 100644 --- a/extensions/noi-ask/manifest.json +++ b/extensions/noi-ask/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "@noi/ask", - "version": "0.1.17", + "version": "0.1.18", "homepage": "https://github.com/lencx/Noi/tree/main/extensions/noi-ask", "description": "The best assistant for batch asking and quick typing of prompts.", "content_scripts": [ @@ -10,6 +10,7 @@ "https://chatgpt.com/*", "https://sora.com/*", "https://gemini.google.com/*", + "https://grok.com/*", "https://poe.com/*", "https://claude.ai/*", "https://huggingface.co/chat/*", @@ -17,17 +18,17 @@ "https://copilot.microsoft.com/*", "https://groq.com/*", "https://pi.ai/talk/*", - "https://www.coze.com/home/*", + "https://suno.com/*", + "https://notebooklm.google.com/*", + "https://github.com/copilot/*", "https://you.com/*", + "https://www.coze.com/home/*", "https://www.coze.cn/home/*", "https://chatglm.cn/*", "https://www.doubao.com/*", "https://tongyi.aliyun.com/qianwen/*", - "https://suno.com/*", - "https://notebooklm.google.com/*", "https://jimeng.jianying.com/*", "https://chat.deepseek.com/*", - "https://github.com/copilot/*", "https://metaso.cn/*" ], "js": ["main.js"], diff --git a/extensions/noi-export-chatgpt/README.md b/extensions/noi-export-chatgpt/README.md deleted file mode 100644 index 51fa652..0000000 --- a/extensions/noi-export-chatgpt/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @noi/export-chatgpt - -ChatGPT chat history export, supports PDF, Image, and Markdown formats. diff --git a/extensions/noi-export-chatgpt/main.js b/extensions/noi-export-chatgpt/main.js deleted file mode 100644 index eb42257..0000000 --- a/extensions/noi-export-chatgpt/main.js +++ /dev/null @@ -1,174 +0,0 @@ -const icons = { - check: ``, - noCheck: ``, -} - -const QUERY_CHAT_LIST = 'main [role="presentation"] div[data-testid]'; -const QUERY_CHECKBOX_AREA = '.empty\\:hidden div.visible'; -const QUERY_ACTION_BUTTON = '.empty\\:hidden div.visible button'; - -window.noiExport = function() { - const allNodes = Array.from(document.querySelectorAll(QUERY_CHAT_LIST)); - if (!allNodes.length) { - return { - selected: [], - all: [], - }; - } - - return { - selected: window._noiSelectedNodes, - all: allNodes.map((el) => { - const node = el.cloneNode(true); - const msgNode = node.querySelector('[data-message-author-role]'); - if (msgNode) { - msgNode.className = 'whitespace-pre-wrap break-words'; - } - return node; - }), - }; -} - -// ------------------------------ - -window.addEventListener('load', () => { - window._noiSelectedNodes = []; - const debouncedHandleMainChanges = debounce(handleMainChanges, 250); - - const observer = new MutationObserver(mutations => { - mutations.forEach(mutation => { - if (!mutation.target.form) { - debouncedHandleMainChanges(); - } - }); - }); - - const mainElement = document.querySelector('main'); - observer.observe(mainElement, { childList: true, subtree: true }); - - debouncedHandleMainChanges(); - - changeURL(() => { - window._noiSelectedNodes = []; - }); -}) - -function debounce(func, wait) { - let timeout; - return function(...args) { - clearTimeout(timeout); - timeout = setTimeout(() => { - func.apply(this, args); - }, wait); - }; -} - -function createButtonForNode(i, index, nodesMap) { - i.setAttribute('noi-node-id', index); - nodesMap.set(index, i); - - const btnArea = i.querySelector(QUERY_CHECKBOX_AREA); - if (!btnArea) return; - - if (btnArea.querySelector('button.noi-checkbox')) return; - - const defaultBtn = i.querySelector(QUERY_ACTION_BUTTON); - if (!defaultBtn) return; - - const btn = document.createElement('button'); - btn.classList.add(...defaultBtn.classList, 'noi-checkbox'); - btn.innerHTML = icons.noCheck; - btn.setAttribute('data-checked', 'false'); - - btn.onclick = () => { - const isChecked = btn.getAttribute('data-checked') === 'true'; - btn.innerHTML = isChecked ? icons.noCheck : icons.check; - btn.setAttribute('data-checked', `${!isChecked}`); - i.classList.toggle('noi-selected', !isChecked); - updateSelectedNodes(isChecked, i.cloneNode(true), nodesMap); - } - - btnArea.insertBefore(btn, btnArea.firstChild); -} - -function updateSelectedNodes(isChecked, node, nodesMap) { - const nodeId = node.getAttribute('noi-node-id'); - const indexInAll = nodesMap.get(Number(nodeId)); - const indexInSelected = window._noiSelectedNodes.findIndex(n => n.getAttribute('noi-node-id') === nodeId); - - if (!isChecked) { - if (indexInSelected === -1) { - let inserted = false; - for (let i = 0; i < window._noiSelectedNodes.length; i++) { - const currentId = window._noiSelectedNodes[i].getAttribute('noi-node-id'); - if (nodesMap.get(Number(currentId)) > indexInAll) { - window._noiSelectedNodes.splice(i, 0, node); - inserted = true; - break; - } - } - if (!inserted) { - const msgNode = node.querySelector('[data-message-author-role]'); - if (msgNode) { - msgNode.className = 'whitespace-pre-wrap break-words'; - } - window._noiSelectedNodes.push(node); - } - } - } else { - if (indexInSelected > -1) { - window._noiSelectedNodes.splice(indexInSelected, 1); - } - } - window._noiSelectedNodes.sort((a, b) => { - const nodeIdA = a.getAttribute('noi-node-id'); - const nodeIdB = b.getAttribute('noi-node-id'); - return Number(nodeIdA) - Number(nodeIdB); - }); - console.log(window._noiSelectedNodes); -} - -function handleMainChanges() { - const nodesMap = new Map(); - document.querySelectorAll(QUERY_CHAT_LIST) - .forEach((node, index) => createButtonForNode(node, index, nodesMap)); -} - -(function (history) { - function triggerEvent(eventName, state) { - if (typeof history[eventName] === 'function') { - history[eventName]({ state: state, url: window.location.href }); - } - } - - function overrideHistoryMethod(methodName, eventName) { - const originalMethod = history[methodName]; - history[methodName] = function (state, ...rest) { - const result = originalMethod.apply(this, [state, ...rest]); - triggerEvent(eventName, state); - return result; - }; - } - - overrideHistoryMethod('pushState', 'onpushstate'); - overrideHistoryMethod('replaceState', 'onreplacestate'); - - window.addEventListener('popstate', () => { - triggerEvent('onpopstate', null); - }); -})(window.history); - -function changeURL(callback) { - window.history.onpushstate = (event) => { - console.log('pushState called:', event.url); - callback && callback(event); - }; - window.history.onreplacestate = (event) => { - console.log('replaceState called:', event.url); - callback && callback(event); - }; - window.history.onpopstate = (event) => { - console.log('popstate event triggered', window.location.href); - callback && callback(event); - }; -} diff --git a/extensions/noi-export-chatgpt/manifest.json b/extensions/noi-export-chatgpt/manifest.json deleted file mode 100644 index 9968d38..0000000 --- a/extensions/noi-export-chatgpt/manifest.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "manifest_version": 3, - "name": "@noi/export-chatgpt", - "version": "0.1.1", - "homepage": "https://github.com/lencx/Noi/tree/main/extensions/noi-export-chatgpt", - "description": "ChatGPT chat history export, supports PDF, Image, and Markdown formats.", - "content_scripts": [ - { - "matches": ["https://chatgpt.com/*"], - "js": ["main.js"], - "css": ["style.css"], - "run_at": "document_end", - "world": "MAIN" - } - ] -} \ No newline at end of file diff --git a/extensions/noi-export-chatgpt/style.css b/extensions/noi-export-chatgpt/style.css deleted file mode 100644 index b5fb7f9..0000000 --- a/extensions/noi-export-chatgpt/style.css +++ /dev/null @@ -1,3 +0,0 @@ -.noi-selected { - border: dashed 2px rgb(34 197 94); -} \ No newline at end of file diff --git a/extensions/noi.extensions.json b/extensions/noi.extensions.json index 42e5f32..4886944 100644 --- a/extensions/noi.extensions.json +++ b/extensions/noi.extensions.json @@ -5,7 +5,7 @@ { "name": "@noi/ask", "description": "The best assistant for batch asking and quick typing of prompts.", - "version": "0.1.17", + "version": "0.1.18", "url": "https://github.com/lencx/Noi/tree/main/extensions/noi-ask", "dirname": "noi-ask", "disabled": false @@ -18,14 +18,6 @@ "dirname": "noi-ask-custom", "disabled": false }, - { - "name": "@noi/export-chatgpt", - "description": "ChatGPT chat history export, supports PDF, Image, and Markdown formats.", - "version": "0.1.1", - "url": "https://github.com/lencx/Noi/tree/main/extensions/noi-export-chatgpt", - "dirname": "noi-export-chatgpt", - "disabled": false - }, { "name": "@noi/reset", "description": "Reset certain website styles to enhance compatibility with Noi.",