Model: Default (GPT-3.5)
Avatar of the person chatting

我正在实现一个批量删除 chatGPT 页面左侧对话的chrome插件。 点击 chrome 插件图标,弹出两个按钮,一个是添加复选框,一个是批量删除。我点击添加复选框按钮后,左边栏上在每个对话前都出现一个复选框,我选中想删除的多个对话,然后我点击批量删除按钮,然后模拟挨个点击对话,再模拟点击每个对话上的删除 button 并点确认按钮来依次删除对话,这样就实现了批量删除对话的功能。 删除对话的代码如下: async function bulkDeleteConversations() { const conversationCheckboxes = document.querySelectorAll('.conversation-checkbox:checked'); const waitForElement = (selector, timeout = 5000) => { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (!mutation.addedNodes) return; for (const node of mutation.addedNodes) { if (node.matches && node.matches(selector)) { observer.disconnect(); resolve(node); return; } } }); }); setTimeout(() => { observer.disconnect(); reject(new Error('Element not found.')); }, timeout); observer.observe(document.body, { childList: true, subtree: true }); }); }; for (let i = 0; i < conversationCheckboxes.length; i++) { const checkbox = conversationCheckboxes[i]; const conversationElement = checkbox.closest('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversationElement.click(); // Click the conversation first await new Promise(resolve => setTimeout(resolve, 500)); // Add a delay of 0.5 second const deleteButton = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible button:nth-child(2)'); // Updated delete button selector if (deleteButton) { deleteButton.click(); // Click the delete button await waitForElement('.absolute.flex.right-1.z-10.text-gray-300.visible'); const buttonsContainer = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible'); const confirmButton = buttonsContainer.querySelector('button:nth-child(1)'); // Get the first button as confirm button if (confirmButton) { console.log("before confirm button click"); confirmButton.click(); console.log("after confirm button click"); await new Promise(resolve => setTimeout(resolve, 1000)); // Add a delay of 1 second } } // Wait for 1 second before deleting the next conversation await new Promise(resolve => setTimeout(resolve, 1000)); } } bulkDeleteConversations(); 现在有一个问题是,页面刷新后,复选框就没了,因此即使我选中了多个对话,最后也只能已成功删除第一个。 如果在删除每个对话之前,获取所有的复选框元素,存储它们的状态,然后在页面刷新后重新创建并勾选之前选中的对话。是不是就能解决?你能帮我改代码吗?

当然可以,我理解了您的问题。在删除每个对话之前,我们可以把选中的复选框的索引保存在一个数组中。然后,在删除对话后,我们重新创建复选框并根据保存的索引状态勾选它们。以下是修改后的代码:

javascriptCopy code
async function bulkDeleteConversations() { const conversationCheckboxes = document.querySelectorAll('.conversation-checkbox:checked'); const checkedIndexes = Array.from(conversationCheckboxes).map(checkbox => parseInt(checkbox.dataset.index)); // 其他代码保持不变 // 在循环结束后,重新添加复选框并设置它们的状态 await addCheckboxesWithState(checkedIndexes); } async function addCheckboxesWithState(checkedIndexes) { // 在这里重新添加复选框 const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } conversation.insertAdjacentElement('afterbegin', checkbox); }); } bulkDeleteConversations();

首先,我们在 bulkDeleteConversations 函数中获取所有选中的复选框的索引并保存在一个数组中。然后,在删除所有选中的对话后,我们调用 addCheckboxesWithState 函数重新添加复选框并根据之前保存的索引状态勾选它们。

这样一来,在刷新页面后,之前选中的对话将仍然保持选中状态。

Avatar of the person chatting

又有个问题,刷新页面后,一个对话前会出现两个复选框。下面这个函数是现在创建复选框的代码: function addCheckboxes() { const conversations = document.querySelectorAll( '.flex.flex-col.gap-2.pb-2.text-gray-100.text-sm > .flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group' ); conversations.forEach((conversation) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertBefore(checkbox, conversation.firstChild); }); } addCheckboxes(); 另外,popup.js 里的代码是这样的: document.getElementById('add-checkboxes').addEventListener('click', () => { chrome.tabs.executeScript({ file: 'addCheckboxes.js' }); }); document.getElementById('bulk-delete').addEventListener('click', () => { chrome.tabs.executeScript({ file: 'bulkDeleteConversations.js' }); });

这个问题的原因是每次点击 "添加复选框" 按钮时,addCheckboxes 函数都会再次执行,导致每个对话前都添加了新的复选框。为了解决这个问题,我们可以在 addCheckboxes 函数中检查每个对话前是否已经存在复选框,如果不存在,则添加复选框。

下面是修改后的 addCheckboxes 函数:

javascriptCopy code
function addCheckboxes() { const conversations = document.querySelectorAll( '.flex.flex-col.gap-2.pb-2.text-gray-100.text-sm > .flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group' ); conversations.forEach((conversation) => { const existingCheckbox = conversation.querySelector('.conversation-checkbox'); if (!existingCheckbox) { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertBefore(checkbox, conversation.firstChild); } }); } addCheckboxes();

conversations.forEach 循环中,我们使用 querySelector 检查每个对话元素中是否已存在一个具有类名 conversation-checkbox 的元素。如果不存在,我们将添加新的复选框。这样,每次执行 addCheckboxes 函数时,只有那些尚未添加复选框的对话才会添加新的复选框,从而避免了重复添加的问题。

Avatar of the person chatting

bulkDeleteConversations.js 代码如下: async function bulkDeleteConversations() { const conversationCheckboxes = document.querySelectorAll('.conversation-checkbox:checked'); const checkedIndexes = Array.from(conversationCheckboxes).map(checkbox => parseInt(checkbox.dataset.index)); const waitForElement = (selector, timeout = 5000) => { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (!mutation.addedNodes) return; for (const node of mutation.addedNodes) { if (node.matches && node.matches(selector)) { observer.disconnect(); resolve(node); return; } } }); }); setTimeout(() => { observer.disconnect(); reject(new Error('Element not found.')); }, timeout); observer.observe(document.body, { childList: true, subtree: true }); }); }; for (let i = 0; i < conversationCheckboxes.length; i++) { const checkbox = conversationCheckboxes[i]; const conversationElement = checkbox.closest('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversationElement.click(); // Click the conversation first await new Promise(resolve => setTimeout(resolve, 500)); // Add a delay of 0.5 second const deleteButton = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible button:nth-child(2)'); // Updated delete button selector if (deleteButton) { deleteButton.click(); // Click the delete button await waitForElement('.absolute.flex.right-1.z-10.text-gray-300.visible'); const buttonsContainer = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible'); const confirmButton = buttonsContainer.querySelector('button:nth-child(1)'); // Get the first button as confirm button if (confirmButton) { console.log("before confirm button click"); confirmButton.click(); console.log("after confirm button click"); await new Promise(resolve => setTimeout(resolve, 1000)); // Add a delay of 1 second } } // Wait for 1 second before deleting the next conversation await new Promise(resolve => setTimeout(resolve, 1000)); await addCheckboxesWithState(checkedIndexes); } } async function addCheckboxesWithState(checkedIndexes) { // 在这里重新添加复选框 const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } conversation.insertAdjacentElement('afterbegin', checkbox); }); } bulkDeleteConversations(); 这里的代码也需要改吧?

是的,这里的代码也需要调整。我们需要在 bulkDeleteConversations 函数内部的循环中先删除当前选中的对话的索引,然后在循环结束后,调用 addCheckboxesWithState 函数。以下是修改后的 bulkDeleteConversations.js 代码:

javascriptCopy code
async function bulkDeleteConversations() { const conversationCheckboxes = document.querySelectorAll('.conversation-checkbox:checked'); const checkedIndexes = Array.from(conversationCheckboxes).map(checkbox => parseInt(checkbox.dataset.index)); // 其他代码保持不变 for (let i = 0; i < conversationCheckboxes.length; i++) { const checkbox = conversationCheckboxes[i]; const conversationElement = checkbox.closest('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversationElement.click(); // Click the conversation first await new Promise(resolve => setTimeout(resolve, 500)); // Add a delay of 0.5 second const deleteButton = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible button:nth-child(2)'); // Updated delete button selector if (deleteButton) { deleteButton.click(); // Click the delete button await waitForElement('.absolute.flex.right-1.z-10.text-gray-300.visible'); const buttonsContainer = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible'); const confirmButton = buttonsContainer.querySelector('button:nth-child(1)'); // Get the first button as confirm button if (confirmButton) { console.log("before confirm button click"); confirmButton.click(); console.log("after confirm button click"); await new Promise(resolve => setTimeout(resolve, 1000)); // Add a delay of 1 second } } // 在这里删除当前选中的对话的索引 const currentIndex = parseInt(checkbox.dataset.index); const indexToRemove = checkedIndexes.indexOf(currentIndex); if (indexToRemove > -1) { checkedIndexes.splice(indexToRemove, 1); } // Wait for 1 second before deleting the next conversation await new Promise(resolve => setTimeout(resolve, 1000)); } // 在循环结束后,重新添加复选框并设置它们的状态 await addCheckboxesWithState(checkedIndexes); } bulkDeleteConversations();

现在,在删除每个对话之后,我们会从 checkedIndexes 数组中删除当前选中的对话的索引。在 bulkDeleteConversations 函数的循环结束后,我们调用 addCheckboxesWithState 函数来重新添加复选框并设置它们的状态。这样,当页面刷新后,之前选中的对话将仍然保持选中状态。

Avatar of the person chatting

直接把 bulkDeleteConversations.js 的完整代码发出来

以下是完整的 bulkDeleteConversations.js 代码:

javascriptCopy code
async function bulkDeleteConversations() { const conversationCheckboxes = document.querySelectorAll('.conversation-checkbox:checked'); const checkedIndexes = Array.from(conversationCheckboxes).map(checkbox => parseInt(checkbox.dataset.index)); const waitForElement = (selector, timeout = 5000) => { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (!mutation.addedNodes) return; for (const node of mutation.addedNodes) { if (node.matches && node.matches(selector)) { observer.disconnect(); resolve(node); return; } }); }); setTimeout(() => { observer.disconnect(); reject(new Error('Element not found.')); }, timeout); observer.observe(document.body, { childList: true, subtree: true }); }); }; for (let i = 0; i < conversationCheckboxes.length; i++) { const checkbox = conversationCheckboxes[i]; const conversationElement = checkbox.closest('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversationElement.click(); // Click the conversation first await new Promise(resolve => setTimeout(resolve, 500)); // Add a delay of 0.5 second const deleteButton = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible button:nth-child(2)'); // Updated delete button selector if (deleteButton) { deleteButton.click(); // Click the delete button await waitForElement('.absolute.flex.right-1.z-10.text-gray-300.visible'); const buttonsContainer = document.querySelector('.absolute.flex.right-1.z-10.text-gray-300.visible'); const confirmButton = buttonsContainer.querySelector('button:nth-child(1)'); // Get the first button as confirm button if (confirmButton) { console.log("before confirm button click"); confirmButton.click(); console.log("after confirm button click"); await new Promise(resolve => setTimeout(resolve, 1000)); // Add a delay of 1 second } } // 在这里删除当前选中的对话的索引 const currentIndex = parseInt(checkbox.dataset.index); const indexToRemove = checkedIndexes.indexOf(currentIndex); if (indexToRemove > -1) { checkedIndexes.splice(indexToRemove, 1); } // Wait for 1 second before deleting the next conversation await new Promise(resolve => setTimeout(resolve, 1000)); } // 在循环结束后,重新添加复选框并设置它们的状态 await addCheckboxesWithState(checkedIndexes); } async function addCheckboxesWithState(checkedIndexes) { // 在这里重新添加复选框 const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true;
Avatar of the person chatting

continue

arduinoCopy code
} conversation.insertAdjacentElement('afterbegin', checkbox);

}); }

bulkDeleteConversations();

goCopy code
这是完整的 `bulkDeleteConversations.js` 代码。现在,在删除每个对话后,这个脚本会根据先前选中的对话复选框的状态重新添加复选框。每次删除一个对话,它都会从 `checkedIndexes` 数组中删除当前选中的对话索引。在循环结束后,它将根据剩余的选中索引重新添加复选框。
Avatar of the person chatting

const observer = new MutationObserver((mutations) =&gt; { mutations.forEach((mutation) =&gt; { if (!mutation.addedNodes) return; for (const node of mutation.addedNodes) { if (node.matches &amp;&amp; node.matches(selector)) { observer.disconnect(); resolve(node); return; } }); }); 这里有语法错误。expected token ')'

抱歉,这里确实有一个语法错误。在 forEach 的括号之前多了一个括号。下面是修复后的代码片段:

javascriptCopy code
const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (!mutation.addedNodes) return; for (const node of mutation.addedNodes) { if (node.matches && node.matches(selector)) { observer.disconnect(); resolve(node); return; } } }); });

这段代码中的 forEach 已经正确闭合,语法错误已修复。

Avatar of the person chatting

成功删除了一个对话后,页面刷新,复选框加上了,但是之前选中的对话并没有自动选中

抱歉,我在之前的代码中犯了一个错误。在 addCheckboxesWithState 函数中,我忘记了为复选框添加事件监听器以防止点击复选框时触发对话点击事件。请更新 addCheckboxesWithState 函数如下:

javascriptCopy code
async function addCheckboxesWithState(checkedIndexes) { // 在这里重新添加复选框 const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } // 添加事件监听器以防止点击复选框时触发对话点击事件 checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); }); }

现在,在删除对话并刷新页面后,之前选中的对话应该会自动选中。

Avatar of the person chatting

还是不行。是不是和页面刷新的时机有关系。

您是对的,可能与页面刷新的时机有关。在删除每个对话之后,我们需要确保在添加复选框之前等待页面刷新完成。为此,我们可以在 bulkDeleteConversations 函数中添加一个新的 waitForElement 调用,以等待页面上重新出现对话列表。请将以下代码添加到 bulkDeleteConversations 函数中的 await addCheckboxesWithState(checkedIndexes); 之前:

javascriptCopy code
// 等待页面刷新并重新显示对话列表 await waitForElement('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group');

修改后的 bulkDeleteConversations 函数应如下所示:

javascriptCopy code
async function bulkDeleteConversations() { // ...其他代码... for (let i = 0; i < conversationCheckboxes.length; i++) { // ...其他代码... // Wait for 1 second before deleting the next conversation await new Promise(resolve => setTimeout(resolve, 1000)); // 等待页面刷新并重新显示对话列表 await waitForElement('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); await addCheckboxesWithState(checkedIndexes); } } // ...其他代码...

现在,脚本应该会在页面刷新并重新显示对话列表后添加复选框,并根据先前选中的对话状态正确选中它们。

Avatar of the person chatting

页面刷新后重新了显示对话列表,有了复选框,但并没有选中

非常抱歉给您带来的困扰。在此,我为您提供一个更新后的解决方案,该解决方案应该能在删除每个对话后正确地选中之前选中的对话。

我们将使用 chrome.storage.local 来存储和检索选中的复选框的索引。这样,我们可以确保即使在页面刷新之后,之前选中的对话仍然选中。

首先,我们需要更新 addCheckboxesWithState 函数,使其从 chrome.storage.local 中获取选中的索引:

javascriptCopy code
async function addCheckboxesWithState() { // 获取存储在 chrome.storage.local 中的选中索引 const checkedIndexes = await new Promise((resolve) => { chrome.storage.local.get(['checkedIndexes'], (result) => { resolve(result.checkedIndexes || []); }); }); // 在这里重新添加复选框 const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } // 添加事件监听器以防止点击复选框时触发对话点击事件 checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); }); }

接下来,我们需要在 bulkDeleteConversations 函数中更新选中索引的存储。将以下代码添加到 await addCheckboxesWithState(); 之前:

javascriptCopy code
// 更新存储在 chrome.storage.local 中的选中索引 chrome.storage.local.set({ checkedIndexes });

这是更新后的 bulkDeleteConversations 函数:

javascriptCopy code
async function bulkDeleteConversations() { // ...其他代码... for (let i = 0; i < conversationCheckboxes.length; i++) { // ...其他代码... // Wait for 1 second before deleting the next conversation await new Promise(resolve => setTimeout(resolve, 1000)); // 等待页面刷新并重新显示对话列表 await waitForElement('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); // 更新存储在 chrome.storage.local 中的选中索引 chrome.storage.local.set({ checkedIndexes }); await addCheckboxesWithState(); } } // ...其他代码...

现在,插件应该能在删除每个对话并刷新页面后正确地选中之前选中的对话。请注意,您可能需要在插件的 manifest.json 文件中添加 "storage" 权限。

Avatar of the person chatting

如何在插件的 manifest.json 文件中添加 "storage" 权限

要在插件的 manifest.json 文件中添加 "storage" 权限,请在文件中找到或添加 "permissions" 键,并将 "storage" 添加到其值数组中。这是一个包含 "storage" 权限的示例 manifest.json 文件:

jsonCopy code
{ "manifest_version": 2, "name": "Your Extension Name", "version": "1.0.0", "description": "Your extension description", "permissions": [ "storage" ], "browser_action": { "default_popup": "popup.html" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content_script.js"] } ] }

请注意,这是一个示例 manifest.json 文件,您需要根据自己的插件进行相应的调整。核心部分是添加了 "permissions" 键,其中包含一个数组,数组内包含 "storage" 权限。这将允许您的插件使用 chrome.storage.local API。

Avatar of the person chatting

还是不行

抱歉,我之前的回答没有考虑到 chrome.storage API 无法在内容脚本中直接使用的限制。为了解决这个问题,我们需要使用消息传递将选中的复选框索引从内容脚本发送到背景脚本,并在背景脚本中存储和检索这些索引。

首先,在 manifest.json 中添加一个背景脚本:

jsonCopy code
{ ... "background": { "scripts": ["background.js"], "persistent": false }, ... }

接着,创建一个名为 background.js 的新文件,并添加以下代码来监听来自内容脚本的消息:

javascriptCopy code
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === 'setCheckedIndexes') { chrome.storage.local.set({ checkedIndexes: request.checkedIndexes }); } else if (request.type === 'getCheckedIndexes') { chrome.storage.local.get(['checkedIndexes'], (result) => { sendResponse(result.checkedIndexes || []); }); return true; } });

现在,我们需要更新 bulkDeleteConversations 函数中的存储选中索引的代码。将以下代码添加到 await addCheckboxesWithState(); 之前:

javascriptCopy code
// 将选中索引发送到背景脚本以存储 chrome.runtime.sendMessage({ type: 'setCheckedIndexes', checkedIndexes });

最后,更新 addCheckboxesWithState 函数以从背景脚本中获取选中的索引:

javascriptCopy code
async function addCheckboxesWithState() { // 从背景脚本获取存储的选中索引 const checkedIndexes = await new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'getCheckedIndexes' }, (response) => { resolve(response); }); }); // 在这里重新添加复选框 const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } // 添加事件监听器以防止点击复选框时触发对话点击事件 checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); }); }

现在,您的插件应该能够在删除每个对话并刷新页面后正确地选中之前选中的对话。

Avatar of the person chatting

仍然没有自动选中

抱歉再次给您带来困扰。在此基础上进行调整,我们可以在addCheckboxesWithState函数内部使用MutationObserver来观察对话列表的变化。当新的对话加载完毕后,我们可以根据存储的选中索引自动选中复选框。

首先,我们需要对addCheckboxesWithState函数进行修改:

javascriptCopy code
async function addCheckboxesWithState() { const checkedIndexes = await new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'getCheckedIndexes' }, (response) => { resolve(response); }); }); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); }); observer.disconnect(); } }); }); observer.observe(document.querySelector('.flex.flex-col.gap-2.pb-2.text-gray-100.text-sm'), { childList: true }); }

这段代码中,我们使用MutationObserver观察对话列表元素的子节点变化。当新的对话被添加到列表时,我们会为它们添加复选框并根据之前存储的选中索引自动选中复选框。

请尝试更新addCheckboxesWithState函数并再次测试插件。现在,您的插件应该能在删除对话并刷新页面后正确地选中之前选中的对话。

Avatar of the person chatting

这下更不行了。连页面刷新后,都没有展示复选框

对于上述代码,我们来进行一些调整。首先,在addCheckboxesWithState函数中,我们需要确保添加复选框时,不会重复添加。此外,我们需要确保在正确的时机观察对话列表元素的子节点变化。

请尝试以下代码:

javascriptCopy code
async function addCheckboxesWithState() { const checkedIndexes = await new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'getCheckedIndexes' }, (response) => { resolve(response); }); }); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); if(conversations.length > 0) { conversations.forEach((conversation, index) => { if (!conversation.querySelector('.conversation-checkbox')) { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); } }); observer.disconnect(); } } }); }); observer.observe(document.querySelector('.flex.flex-col.gap-2.pb-2.text-gray-100.text-sm'), { childList: true }); }

在这个版本的addCheckboxesWithState函数中,我们添加了一个条件判断,确保不会重复添加复选框。

现在,请再次尝试运行插件。页面刷新后应该可以看到复选框,并且之前选中的对话应该已经被自动选中。

Avatar of the person chatting

还是没有重新出现复选框

非常抱歉给您带来了困扰。让我们对代码进行进一步的调整。此次我们将使用setTimeout在一定的时间间隔后检查对话列表是否已经更新,并尝试添加复选框。

请尝试以下代码:

javascriptCopy code
async function addCheckboxesWithState() { const checkedIndexes = await new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'getCheckedIndexes' }, (response) => { resolve(response); }); }); const addCheckboxesInterval = setInterval(() => { const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); if (conversations.length > 0) { conversations
Avatar of the person chatting

还是没有出现复选框

抱歉再次给您带来了困扰。让我们尝试一个新的方法来解决这个问题。这次我们将使用requestAnimationFrame来在下一帧渲染时检查对话列表是否已经更新,并尝试添加复选框。

请尝试以下代码:

javascriptCopy code
async function addCheckboxesWithState() { const checkedIndexes = await new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'getCheckedIndexes' }, (response) => { resolve(response); }); }); function addCheckboxes() { const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); if (conversations.length > 0) { conversations.forEach((conversation, index) => { if (!conversation.querySelector('.conversation-checkbox')) { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); } }); } else { requestAnimationFrame(addCheckboxes); } } addCheckboxes(); }

这个版本的addCheckboxesWithState函数使用了requestAnimationFrame来在下一帧渲染时尝试添加复选框。这样,在页面刷新后,复选框应该会正确地显示出来,而且之前选中的对话应该已经被自动选中。

Avatar of the person chatting

页面刷新后,有复选框了,但是并没有重新选中

非常抱歉给您带来了困扰。我们再次调整代码,使用 MutationObserver 来实现在对话列表更新时添加复选框。

javascriptCopy code
async function addCheckboxesWithState() { const checkedIndexes = await new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'getCheckedIndexes' }, (response) => { resolve(response); }); }); function addCheckboxes() { const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); conversations.forEach((conversation, index) => { if (!conversation.querySelector('.conversation-checkbox')) { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); } }); } const listObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { addCheckboxes(); } } }); const conversationList = document.querySelector('.flex.flex-col.gap-2.pb-2.text-gray-100.text-sm'); if (conversationList) { listObserver.observe(conversationList, { childList: true, subtree: true }); } } addCheckboxesWithState();

这个版本的 addCheckboxesWithState 函数使用了 MutationObserver 来监听对话列表的变化。当对话列表更新时,addCheckboxes 函数将被调用,复选框将被添加到对话中,并根据存储的选中索引进行自动选中。

Avatar of the person chatting

这下又没有复选框了。你看不是加点日志啥的排查下

确实,为了更好地了解问题,我们可以在代码中添加一些日志。这样我们就可以观察到代码的执行过程以及潜在的问题。请将以下代码粘贴到 addCheckboxesWithState 函数中:

javascriptCopy code
async function addCheckboxesWithState() { console.log("addCheckboxesWithState called"); const checkedIndexes = await new Promise((resolve) => { chrome.runtime.sendMessage({ type: 'getCheckedIndexes' }, (response) => { resolve(response); }); }); function addCheckboxes() { console.log("addCheckboxes called"); const conversations = document.querySelectorAll('.flex.py-3.px-3.items-center.gap-3.relative.rounded-md.hover\\:bg-\\[\\#2A2B32\\].cursor-pointer.break-all.hover\\:pr-4.group'); if (conversations.length === 0) { console.log("No conversations found"); } conversations.forEach((conversation, index) => { if (!conversation.querySelector('.conversation-checkbox')) { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'conversation-checkbox'; checkbox.dataset.index = index; if (checkedIndexes.includes(index)) { checkbox.checked = true; } checkbox.addEventListener('click', (e) => { e.stopPropagation(); }); conversation.insertAdjacentElement('afterbegin', checkbox); } }); } const listObserver = new MutationObserver((mutations) => { console.log("Mutation observed"); for (const mutation of mutations) { if (mutation.type === 'childList') { console.log("ChildList mutation observed"); addCheckboxes(); } } }); const conversationList = document.querySelector('.flex.flex-col.gap-2.pb-2.text-gray-100.text-sm'); if (conversationList) { console.log("Conversation list found"); listObserver.observe(conversationList, { childList: true, subtree: true }); } else { console.log("Conversation list not found"); } } addCheckboxesWithState();

然后,在 Chrome 开发者工具的控制台(Console)中查看输出的日志。这将帮助我们找出问题所在。

Avatar of the person chatting

ChildList mutation observed bulkDeleteConversations.js:91 addCheckboxes called bulkDeleteConversations.js:119 Mutation observed bulkDeleteConversations.js:122 ChildList mutation observed bulkDeleteConversations.js:91 addCheckboxes called bulkDeleteConversations.js:122 ChildList mutation observed bulkDeleteConversations.js:91 addCheckboxes called bulkDeleteConversations.js:122 ChildList mutation observed bulkDeleteConversations.js:91 addCheckboxes called bulkDeleteConversations.js:122 ChildList mutation observed bulkDeleteConversations.js:91 addCheckboxes called bulkDeleteConversations.js:82 addCheckboxesWithState called bulkDeleteConversations.js:130 Conversation list found

从日志来看,addCheckboxesWithState 函数在页面刷新后被调用了,addCheckboxes 函数也被多次调用。但是,我们需要知道 checkedIndexes 是否包含正确的值。请将以下日志添加到 addCheckboxesWithState 函数的开头,以打印 checkedIndexes 的值:

javascriptCopy code
console.log("Checked indexes:", checkedIndexes);

此外,为了确保 MutationObserver 正确观察到了列表的变化,我们可以尝试将 listObserver.observe 的参数稍作修改,只观察 childList 变化,而不观察 subtree 变化。请将以下代码替换现有的 listObserver.observe 调用:

javascriptCopy code
listObserver.observe(conversationList, { childList: true });

然后再次尝试删除对话并查看控制台输出。

Avatar of the person chatting

before confirm button click bulkDeleteConversations.js:55 after confirm button click bulkDeleteConversations.js:83 addCheckboxesWithState called bulkDeleteConversations.js:92 Checked indexes: []length: 0[[Prototype]]: Array(0) bulkDeleteConversations.js:134 Conversation list found

从日志中可以看出,checkedIndexes 为空数组,这意味着之前选中的对话索引未正确存储。为了解决这个问题,请在 bulkDeleteConversations.js 文件中的 bulkDeleteConversations 函数内添加以下代码,将选中的对话索引存储到插件的存储区域:

javascriptCopy code
chrome.runtime.sendMessage({ type: 'setCheckedIndexes', checkedIndexes: checkedIndexes });

将此代码添加到 const checkedIndexes = Array.from(conversationCheckboxes).map(checkbox => parseInt(checkbox.dataset.index)); 之后。

然后再次尝试删除对话并查看控制台输出。

Avatar of the person chatting

Conversation list found bulkDeleteConversations.js:56 before confirm button click bulkDeleteConversations.js:58 after confirm button click bulkDeleteConversations.js:86 addCheckboxesWithState called bulkDeleteConversations.js:95 Checked indexes: [] bulkDeleteConversations.js:137 Conversation list found

看起来 checkedIndexes 仍然是一个空数组。为了解决这个问题,我们需要确保正确处理来自 background.js 的响应。请在 bulkDeleteConversations.js 文件中将 chrome.runtime.onMessage 替换为 chrome.runtime.onMessage.addListener

javascriptCopy code
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === 'checkedIndexes') { checkedIndexes = request.checkedIndexes; addCheckboxesWithState(checkedIndexes); } });

然后再次尝试删除对话并查看控制台输出。

Avatar of the person chatting

在 bulkDeleteConversations.js 文件中,并没有这和代码 chrome.runtime.onMessage

抱歉,我的失误。请在 bulkDeleteConversations.js 文件中添加以下代码:

javascriptCopy code
let checkedIndexes = []; chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === 'checkedIndexes') { checkedIndexes = request.checkedIndexes; addCheckboxesWithState(checkedIndexes); } });

将这段代码添加到文件的开头,并确保在 bulkDeleteConversations 函数内发送消息:

javascriptCopy code
chrome.runtime.sendMessage({ type: 'setCheckedIndexes', checkedIndexes: checkedIndexes });

现在再次尝试删除对话并查看控制台输出。

Avatar of the person chatting

caught SyntaxError: Identifier 'checkedIndexes' has already been declared (at bulkDeleteConversations.js:1:1) 报错

抱歉,我的失误。请将 bulkDeleteConversations.js 文件开头的 let checkedIndexes = []; 声明移除,然后更新 chrome.runtime.onMessage.addListener 代码,将 checkedIndexes 声明移至回调函数内:

javascriptCopy code
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === 'checkedIndexes') { let checkedIndexes = request.checkedIndexes; addCheckboxesWithState(checkedIndexes); } });

现在再次尝试删除对话并查看控制台输出。

Avatar of the person chatting

before confirm button click bulkDeleteConversations.js:68 after confirm button click bulkDeleteConversations.js:96 addCheckboxesWithState called bulkDeleteConversations.js:105 Checked indexes: [] bulkDeleteConversations.js:147 Conversation list found

从输出来看,checkedIndexes 仍然是一个空数组。为了确保 background.js 正确发送 checkedIndexes,让我们检查 background.js 中是否正确发送了 checkedIndexes

请在 background.js 中添加以下日志输出,以便在发送消息之前查看 checkedIndexes

javascriptCopy code
console.log('Sending checkedIndexes from background:', checkedIndexes); chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { chrome.tabs.sendMessage(tabs[0].id, { type: 'checkedIndexes', checkedIndexes: checkedIndexes }); });

然后,再次尝试删除对话并查看控制台输出。

Avatar of the person chatting

lkDeleteConversations.js:123 Uncaught TypeError: Cannot read properties of undefined (reading 'includes') at bulkDeleteConversations.js:123:28 at NodeList.forEach () at addCheckboxes (bulkDeleteConversations.js:116:19) at MutationObserver. (bulkDeleteConversations.js:140:9)

看起来我们在使用 checkedIndexes 时遇到了一个错误。为了解决这个问题,请更新 addCheckboxes 函数,添加一个名为 checkedIndexes 的可选参数:

javascriptCopy code
function addCheckboxes(checkedIndexes = []) { // ... }

然后,请更新 addCheckboxesWithState 函数,以将 checkedIndexes 传递给 addCheckboxes 函数:

javascriptCopy code
async function addCheckboxesWithState(checkedIndexes) { // ... addCheckboxes(checkedIndexes); // ... }

现在,再次尝试删除对话并查看控制台输出。

Avatar of the person chatting

checked runtime.lastError: Could not establish connection. Receiving end does not exist.

这个错误通常发生在没有找到要与之通信的标签页时。请检查以下内容:

  1. 确保您已经打开了要使用插件的标签页。
  2. 确保您在正确的标签页中测试插件。

如果问题仍然存在,请尝试重新加载插件,然后再次尝试。

Avatar of the person chatting

我准备把这个插件发布到 chrome store,现在让填说明: 请着重说明该内容的用途以及用户为何应该安装它。请用英文回答。

This Chrome extension is designed to help users delete conversations in ChatGPT's left sidebar quickly and efficiently. With the extension installed, users can add checkboxes to each conversation in the sidebar, select multiple conversations to delete, and then delete them with just a few clicks.

This extension is particularly useful for users who frequently use ChatGPT and need to manage their conversations efficiently. Instead of deleting conversations one by one, they can delete multiple conversations at once, saving time and effort.

Overall, this extension provides a simple and intuitive solution for ChatGPT users to manage their conversations effectively.

Avatar of the person chatting

翻译成中文

这个 Chrome 插件旨在帮助用户快速高效地删除 ChatGPT 左侧边栏中的对话。安装了这个插件后,用户可以在边栏中为每个对话添加复选框,选择多个要删除的对话,然后只需点击几下就可以删除它们。

该插件特别适用于经常使用 ChatGPT 并需要高效管理对话的用户。他们可以一次性删除多个对话,而不是一个一个地删除,从而节省时间和精力。

总体而言,该插件为 ChatGPT 用户提供了一个简单直观的解决方案,以有效地管理他们的对话。

Avatar of the person chatting

有没有在线的可以转换图标到128*128

是的,有很多在线的工具可以转换图标大小。其中一个比较常用的工具是 Favicon.io。您可以上传您的图标并选择要生成的大小,包括 128x128 像素。此外,该工具还提供其他调整选项,如背景透明度和图标类型。