您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Bring missing features to Google Chat.
// ==UserScript== // @name Enhanced Google Chat // @namespace http://tampermonkey.net/ // @version 2023-12-30 // @description Bring missing features to Google Chat. // @author @higuoxing // @license MIT // @match https://mail.google.com/chat/u/* // @match https://chat.google.com/u/* // @icon https://www.google.com/s2/favicons?sz=64&domain=chat.google.com // @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js // @resource REMOTE_CSS https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css // @grant GM_getResourceText // @grant GM_addStyle // ==/UserScript== /* global hljs */ const config = { // Valid options are: // "None": Don't modify the default key bindings of Google Chat. // "Ctrl-Enter": Use "Ctrl-Enter" to send messages and "Enter" to insert a new line. // "Enter": Use "Enter" to send messages and "Ctrl-Enter" to insert a new line. send_message: "Ctrl-Enter", }; function render_code_blocks() { let spans = document.getElementsByTagName("span"); for (let span of spans) { let data_cd_attr = span.getAttribute("data-cd"); // We use <span data-cd="hidden">```</span> to identify code blocks. if (data_cd_attr === "hidden" && span.textContent === "```") { let next_sibling_element = span.nextElementSibling; if (next_sibling_element != null && next_sibling_element.getAttribute("role") === "complementary") { // The next sibling element is the content // of the code block. let parent_div = span.parentElement; let orig_code_block = span.nextElementSibling; // Determine the language. let orig_code_content = orig_code_block.innerText; let orig_code_lines = orig_code_content.split('\n'); if (orig_code_lines.length < 1) { continue; } let language = orig_code_lines[0]; // Check if hljs can highlight our language. if (hljs.getLanguage(language) === undefined || hljs.getLanguage(language) === null) { continue; } // Create code container. let pre_ele = document.createElement("pre"); let code_ele = document.createElement("code"); code_ele.setAttribute("class", "language-" + language); // Remove the 1st line that specifies the language. orig_code_lines.shift(); // Append our new code block. code_ele.textContent = orig_code_lines.join('\n'); pre_ele.appendChild(code_ele); // We're ready to highlight it. hljs.highlightElement(code_ele); let language_mark = document.createElement("span"); language_mark.setAttribute("class", "hljs-language-mark"); language_mark.textContent = language + ":"; // Create a new code container. let code_container = document.createElement("div"); code_container.appendChild(language_mark); code_container.appendChild(pre_ele); // Append it to the parent element. parent_div.insertBefore(code_container, orig_code_block); // Remove the original code block. orig_code_block.remove(); } // Remove the <span> tag so that we won't render the code block twice. span.remove(); } } } function register_enter_key_handler(element, config) { element.setAttribute('enter-key-event-registered', 'true'); element.addEventListener('keydown', (e) => { // Only let it go if the ctrl key is down. // Just don't call preventDefault(), a new line will be created always which is the // textfield's default behaviour. if (e.key == 'Enter' && ((config.send_message == "Ctrl-Enter" && !e.ctrlKey) || (config.send_message == "Enter" && e.ctrlKey))) { // Get the pop up list after inputting "@" (for tagging people) or ':' (for inserting emojis), etc. let div_nodes = document.getElementsByTagName('div'); let list_expanded = false; for (let div of div_nodes) { if (div.getAttribute('role') === 'listbox' && div.getAttribute('data-expanded') === 'true') { // Do not intercept enter key if the pop up list is visible. list_expanded = true; } } if (!list_expanded) { e.stopImmediatePropagation(); } } }, true); } function check_user_config() { if (config.send_message != "None" && config.send_message != "Ctrl-Enter" && config.send_message != "Enter") { alert("Invalid value for config.send_message. Please check enhanced-google-chat.user.js."); } } // Called only once. function initialize() { // Initialize stylesheets. const hljs_css = GM_getResourceText("REMOTE_CSS"); GM_addStyle(hljs_css); // I'm not a CSS expert, we force the font family of every hljs elements to be monospace. GM_addStyle(`[class^="hljs-"], [class*=" hljs-"], [class^="hljs"], [class*=" hljs"] { font-family: "Roboto Mono",monospace; } .hljs-language-mark { font-size: 0.8em; } .hljs { background: #fafafa; } ` ); check_user_config(config); } function modify_key_event() { if (config.send_message != "None") { // register event on the chat text input box let div_nodes = document.getElementsByTagName('div'); for (let div of div_nodes) { // The id of the input text area varies, we use 'role', 'aria-label' and 'contenteditable' attributes // to locate the element. if (div.getAttribute('role') === 'textbox' && div.getAttribute('contenteditable') === 'true' && div.getAttribute('enter-key-event-registered') != 'true') { register_enter_key_handler(div, config); } } } } // Called periodically. function main() { render_code_blocks(); modify_key_event(); } function debounce(fn, delay) { let timeout = null; return function () { if (timeout) { return; } else { timeout = setTimeout(function () { fn(); timeout = null; }, delay); } } } (function () { 'use strict'; if (window.trustedTypes && window.trustedTypes.createPolicy) { window.trustedTypes.createPolicy('default', { createHTML: (string, sink) => string }); } initialize(); let el = document.documentElement; el.addEventListener('DOMSubtreeModified', debounce(main, 1000)); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址