我正在尝试为基于FireFox/Gecko的浏览器开发一个扩展程序,以便在chat.openai.com上添加一个上传文件的按钮。当我添加我的扩展程序时,它只会在刷新一个聊天页面时添加按钮。如果我转到过去的聊天,它就不会添加按钮。(顺便说一下,我在ChatGPT的帮助下编写了这段代码,哈哈)。
manifest.json:
{ "manifest_version": 3, "name": "ChatGPT File Upload", "version": "1.0", "description": "Adds a button to upload files into ChatGPT. (NOT for images, videos, Word Documents, or other non-raw-text files. Please use .txt, .js, .py, .html, .css, .json, and .csv.", "permissions": [ "scripting", "https://chat.openai.com/*" ], "action": { "default_icon": { "128": "icon128.png", "256": "icon128.png" } }, "icons": { "128": "icon128.png", "256": "icon256.png" }, "content_scripts": [ { "matches": ["https://chat.openai.com/*"], "js": ["content.js"] } ], "background": { "scripts": ["background.js"], "service_worker": "background.js" } }
background.js:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (changeInfo.url && changeInfo.url.startsWith('https://chat.openai.com/')) { chrome.scripting.executeScript({ target: { tabId: tabId }, files: ['content.js'] }); } });
content.js:
console.log("Content script loaded.");// This script will be injected into chat.openai.com pages// You can add your desired functionality here// Create the buttonconst button = document.createElement('button');button.innerText = '📂 Submit File';button.style.backgroundColor = '#35393d';button.style.color = 'white';button.style.padding = '5px';button.style.border = '1px solid #6b6458';button.style.borderRadius = '5px';button.style.margin = '5px';button.style.width = '180px';// Create a container div for centeringconst containerDiv = document.createElement('div');containerDiv.style.display = 'flex';containerDiv.style.justifyContent = 'center';// Append the button to the container divcontainerDiv.appendChild(button);// Find the target elementconst targetElement = document.querySelector("div.relative.flex.h-full.max-w-full.flex-1.overflow-hidden > div > main > div.absolute.bottom-0 > form > div > div:nth-child(1)");// Insert the container div before the target elementtargetElement.parentNode.insertBefore(containerDiv, targetElement);// Add click event listener to the buttonbutton.addEventListener('click', async () => { // Create the file input element const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.txt, .js, .py, .html, .css, .json, .csv'; // Handle file selection fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = async (e) => { const fileContent = e.target.result; const chunkSize = 15000; const chunks = []; // Split file content into chunks for (let i = 0; i < fileContent.length; i += chunkSize) { const chunk = fileContent.slice(i, i + chunkSize); chunks.push(chunk); } // Submit each chunk to the conversation for (let i = 0; i < chunks.length; i++) { const chunk = chunks[i]; const part = i + 1; const filename = file.name; await submitConversation(chunk, part, filename); } }; reader.readAsText(file); } }); // Trigger file input click event fileInput.click();});// Submit conversation functionasync function submitConversation(text, part, filename) { const textarea = document.querySelector("textarea[tabindex='0']"); const enterKeyEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, keyCode: 13, }); textarea.value = `Part ${part} of ${filename}:\n\n${text}`; textarea.dispatchEvent(enterKeyEvent);}
我在网上查看了不同的background.js文件,但似乎没有一个能解决我的问题。我对开发非常新手,所以在这类事情上我基本上是迷失的。
回答:
在这里扩展我的评论。
导航可以分为两种类型
- 完整页面刷新
- 基于JavaScript的页面加载
在第一种情况下,扩展程序会执行其任务并加载按钮,但在第二种情况下,如果Web应用程序用新元素加载整个页面,那么你的“编辑”元素将被新元素替换。
据我所知,你可以通过两种方式解决这个问题
- 监听导航变化
- 使用Mutation Observer
监听导航事件
如果Web应用程序更改了地址栏中的地址,那么你可以监听该事件,并在按钮不存在时添加按钮。https://developer.mozilla.org/en-US/docs/Web/API/Navigation/navigate_event
navigation.addEventListener("navigate", (event) => { checkAndInjectButton()})
Mutation Observer
如果由于某些原因你无法检测到Web应用程序中的导航变化,那么你可以监听DOM中的变化,并根据事件做出反应。
Mutation Observer会跟踪属性、子节点和子树,因此如果对目标元素进行了任何更改,你的脚本将获得回调。
// Select the node that will be observed for mutationsconst targetElement = document.querySelector("div.relative.flex.h-full.max-w-full.flex-1.overflow-hidden > div > main > div.absolute.bottom-0 > form > div > div:nth-child(1)");// Options for the observer (which mutations to observe)const config = { attributes: true, childList: true, subtree: true };// Callback function to execute when mutations are observedconst callback = (mutationList, observer) => { checkAndInjectButton();};// Create an observer instance linked to the callback functionconst observer = new MutationObserver(callback);// Start observing the target node for configured mutationsobserver.observe(targetElement, config);// If you need to stop observingobserver.disconnect();
这两种解决方案都将应用到你的content.js