Improves Bing ChatAI user experience by preventing accidental scrolling and increasing input character limit
当前为
// ==UserScript==
// @name Enhanced Bing ChatAI
// @namespace EnhancedBingChatAI
// @description Improves Bing ChatAI user experience by preventing accidental scrolling and increasing input character limit
// @version 1.1.0
// @author CriDos
// @grant GM_setClipboard
// @match https://www.bing.com/*
// @license MIT
// ==/UserScript==
// Prevent scrolling when hovering over cib-serp-main element
window.addEventListener('wheel', (event) => {
if (event.target.className.includes('cib-serp-main')) {
event.stopPropagation();
}
});
(function () {
'use strict';
// Increase input character limit to 100,000
{
const increaseCharacterLimit = () => {
const textareaElement = document.querySelector('#b_sydConvCont > cib-serp').shadowRoot.querySelector('#cib-action-bar-main').shadowRoot.querySelector('#searchboxform > label').querySelector('textarea');
if (textareaElement) textareaElement.setAttribute('maxlength', '100000');
};
const waitForElement = (selector, callback) => {
const element = document.querySelector(selector);
if (element) {
callback();
} else {
setTimeout(() => {
waitForElement(selector, callback);
}, 2000);
}
};
waitForElement('#b_sydConvCont > cib-serp', increaseCharacterLimit);
}
// Copy Code Blocks from Bing Chats
{
// A function that returns a copyCode function with a reference to the code block element
function createCopyCode(codeBlock) {
// Return a copyCode function that does the following:
return function (e) {
// Get the visible text of the code block element passed to the createCopyCode function
let text = codeBlock.innerText;
// Copy the text to the clipboard
GM_setClipboard(text);
// Show a confirmation message using a temporary span element
let span = document.createElement("span");
span.className = "copy-message";
span.textContent = "Copied!";
span.style.color = "green";
span.style.marginLeft = "5px";
span.style.opacity = "1";
e.target.parentElement.appendChild(span);
// Fade away and remove the span element after 3 seconds
setTimeout(function () {
let fade = setInterval(function () {
if (span.style.opacity > 0) {
span.style.opacity -= 0.1;
} else {
clearInterval(fade);
span.remove();
}
}, 100);
}, 1000);
};
}
// A function that traverses the entire DOM tree and checks for shadow roots and code blocks at each node
function traverseDOM(node) {
// If the node has a shadowRoot property, call the function recursively on the shadow root node
if (node.shadowRoot) {
traverseDOM(node.shadowRoot);
}
// If the node has a querySelectorAll method, use it to get all the code blocks under the node and add copy buttons to them if they don't have one already
if (node.querySelectorAll) {
let codeBlocks = node.querySelectorAll("pre > code");
for (let codeBlock of codeBlocks) {
let preCodeBlock = codeBlock.parentElement;
let copyButton = preCodeBlock.querySelector(".copy-button");
if (!copyButton) {
copyButton = document.createElement("button");
copyButton.className = "copy-button";
copyButton.textContent = "Copy";
copyButton.addEventListener("click", createCopyCode(codeBlock));
try {
preCodeBlock.appendChild(copyButton);
} catch (error) {
console.error(error);
}
}
}
}
// If the node has child nodes, loop through them and call the function recursively on each child node
if (node.childNodes) {
for (let child of node.childNodes) {
traverseDOM(child);
}
}
}
// A function that runs when the DOM changes and calls the traverseDOM function
function onDOMChange(mutations) {
// Loop through each mutation record and check if it added or removed any nodes or attributes to the DOM
for (let mutation of mutations) {
if (mutation.type === "childList" || mutation.type === "attributes") {
// Call the traverseDOM function on the target node of the mutation record and any added or removed nodes
traverseDOM(mutation.target);
for (let addedNode of mutation.addedNodes) {
traverseDOM(addedNode);
}
for (let removedNode of mutation.removedNodes) {
traverseDOM(removedNode);
}
}
}
}
// Create a new mutation observer and pass it the onDOMChange function
let observer = new MutationObserver(onDOMChange);
// Start observing the entire document for anytype of changes
observer.observe(document, { childList: true, attributes: true, subtree: true });
// A function that checks if the cib-feedback element is present in the DOM
function isFeedbackReady() {
// Use the traverseDOM function to get all the cib-feedback elements from any shadow roots
let feedbackElements = [];
traverseDOM(document.body, feedbackElements);
// Return true if there is at least one cib-feedback element in the DOM, false otherwise
return feedbackElements.length > 0;
}
// A function that adds a copy button to each code block in bing chats
function addCopyButtons() {
// Get all the code blocks in bing chats using the new selector
let codeBlocks = document.querySelectorAll("pre > code");
// Loop through each code block
for (let codeBlock of codeBlocks) {
let preCodeBlock = codeBlock.parentElement;
// Check if the code block already has a copy button
let copyButton = preCodeBlock.querySelector(".copy-button");
// If not, create a new copy button
if (!copyButton) {
// Create a new button element
copyButton = document.createElement("button");
// Set the class name of the button
copyButton.className = "copy-button";
// Set the text content of the button
copyButton.textContent = "Copy";
// Add a click event listener to the button
copyButton.addEventListener("click", createCopyCode(codeBlock));
// Try to insert the button before the code element
try {
preCodeBlock.appendChild(copyButton);
} catch (error) {
// Catch and log any errors that occur while inserting the button
console.error(error);
}
}
}
}
// A function that runs when the document is ready and calls the addCopyButtons function only if the cib-feedback element is present in the DOM
function onDocumentReady() {
// Check if the cib-feedback element is present in the DOM using the isFeedbackReady function
if (isFeedbackReady()) {
// Call the addCopyButtons function to add copy buttons to all code blocks in bing chats
addCopyButtons();
} else {
// Wait for 100 milliseconds and try again
setTimeout(onDocumentReady, 1000);
}
}
// Run the onDocumentReady function when the document is ready
if (document.readyState === "complete" || document.readyState === "interactive") {
onDocumentReady();
} else {
document.addEventListener("DOMContentLoaded", onDocumentReady);
}
}
})();