您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Redesigned chatbox meant both for power users, and those who maybe just wants something new
当前为
// ==UserScript== // @name Better TankTrouble Chatbox // @author commander // @description Redesigned chatbox meant both for power users, and those who maybe just wants something new // @namespace https://github.com/asger-finding/tanktrouble-userscripts // @version 0.1.2 // @license GPL-3.0 // @match *://*.tanktrouble.com/* // @exclude *://classic.tanktrouble.com/ // @run-at document-end // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @require https://update.greasyfork.org/scripts/482092/1309109/TankTrouble%20Development%20Library.js // @noframes // ==/UserScript== // TODO: chat messages history - press up to go to last sent message // TODO: whisper suggestions when pressing @ ? GM_addStyle(` #chat { /*Move it to the bottom left*/ inset: calc(100% - 30px) auto auto 34px !important; /*Disable drop shadow filter*/ filter: none; -webkit-filter: none; } /*Reverse the chat flow*/ #chat, #chat .content, #chat .body { display: flex; flex-direction: column-reverse; } #chat .status.button { transform: translate(7px, -18px); cursor: initial; z-index: 1; } #chat form { width: 200px; margin-left: 20px; background: #ececec; } #chat form[style*="repeating-linear-gradient"] { background: #d0d0d0 !important; } #chat:not(.open) form { display: none; } #chat textarea { left: 5px; transition: width 0s !important; } #chat .body { padding-right: 10px; border-radius: 3px; background: linear-gradient(225deg, #00000005 12px, #00000014 12px, #00000014 100%); margin-bottom: 7px; top: 0 !important; -webkit-mask-image: linear-gradient(225deg, #000000 11px, #00000000 12px, #00000000 100% ), linear-gradient(to top, #000000 70%, rgba(0, 0, 0, 0.11)); } #chat .body .chatMessage svg { padding: 2px 4px 1px 4px; border-left: 2px dotted rgb(170, 170, 170); filter: drop-shadow(1px 1px 1px #00000022); } #chat .body.dragging { border: none !important; margin-left: 20px !important; } /*Rotate and align the handle to top-right*/ .handle.ui-resizable-ne[src*="resizeHandleBottomRight.png"] { width: 12px; height: 12px !important; transform: translateX(6px) rotate(-90deg); z-index: 2147483647; position: sticky; left: calc(100% - 7px); top: 0; order: 0; margin-bottom: auto !important; } body:has(#chat .body.ui-resizable-resizing) .ui-resizable-handle.handle.ui-resizable-ne { display: none !important; } /* Scrollbar */ #chat .body { scrollbar-gutter: stable; scrollbar-width: thin; scrollbar-color: rgb(170, 170, 170) transparent; align-items: end; direction: rtl; pointer-events: auto; overflow-x: hidden; overflow-y: hidden; } #chat .body:hover { overflow-y: scroll; } #chat .body .chatMessage { direction: ltr; margin-left: ${(/Chrome.*Safari/u).test(navigator.userAgent) ? '3px' : '5px'}; } #chat .body::-webkit-scrollbar { width: 3px; } #chat .body::-webkit-scrollbar-track { background: transparent; } #chat .body::-webkit-scrollbar-thumb { background: rgb(170, 170, 170); } `); /** * Reconfigure the chat handle to be dragging * from the south-east direction (down) * to the north-east direction (up) */ const changeHandleDirection = () => { const { resizable } = $.fn; // Use a regular function to keep context $.fn.resizable = function(...args) { const [config] = args; // Reassign the chat handle to be north-east facing if (config.handles) { const handle = config.handles.se; if (handle === TankTrouble.ChatBox.chatBodyResizeHandle) { handle.removeClass('ui-resizable-se') .addClass('ui-resizable-ne'); config.handles.ne = handle; delete config.handles.se; // Set a taller chat maxHeight config.maxHeight = 650; } } return resizable.call(this, config); }; }; /** * Hook message render functions to disable jquery .show() animation * This fixes chat messages not showing up in the reversed chat order */ const fixChatRendering = () => { Loader.interceptFunction(TankTrouble.ChatBox, '_renderChatMessage', (original, ...args) => { TankTrouble.ChatBox.chatBody.scrollTop(TankTrouble.ChatBox.chatBody.height()); // Set animateHeight to false args[9] = false; original(...args); }); Loader.interceptFunction(TankTrouble.ChatBox, '_renderSystemMessage', (original, ...args) => { TankTrouble.ChatBox.chatBody.scrollTop(TankTrouble.ChatBox.chatBody.height()); // Set animateHeight to false args[3] = false; original(...args); }); }; /** * Prevent TankTrouble from clearing the chat when the client disconnects * Print message to chat when client switches server to separate conversations */ const preventChatClear = () => { Loader.interceptFunction(TankTrouble.ChatBox, '_clearChat', (original, ...args) => { const isUnconnected = ClientManager.getClient().getState() === TTClient.STATES.UNCONNECTED; // Void the call if the client is unconnected // when the function is invoked if (isUnconnected) return null; return original(...args); }); Loader.interceptFunction(TankTrouble.ChatBox, '_updateStatusMessageAndAvailability', (original, ...args) => { const [systemMessageText, guestPlayerIds] = args; // Check for welcome message // If true, print a system message if (systemMessageText === 'Welcome to TankTrouble Comms § § ') { const newServer = ClientManager.getAvailableServers()[ClientManager.multiplayerServerId]; return original(`Connected to ${ newServer.name } ${ guestPlayerIds.length ? '§ ' : '' }`, guestPlayerIds); } return original(...args); }); }; /** * Write the chat savestate to storage and return * @returns Promise for last savestate */ const startChatSavestate = () => { Loader.interceptFunction(TankTrouble.ChatBox, 'open', (original, ...args) => { GM_setValue('chat-open', true); original(...args); }); Loader.interceptFunction(TankTrouble.ChatBox, 'close', (original, ...args) => { GM_setValue('chat-open', false); original(...args); }); // Get savestate and default to chat being open return GM_getValue('chat-open', true); }; changeHandleDirection(); fixChatRendering(); Loader.whenContentInitialized().then(async() => { preventChatClear(); const shouldChatOpen = await startChatSavestate(); if (shouldChatOpen) TankTrouble.ChatBox.open(); // Get the plain nodes for better performance // eslint-disable-next-line prefer-destructuring const chatBody = TankTrouble.ChatBox.chatBody[0]; // eslint-disable-next-line prefer-destructuring const chatForm = TankTrouble.ChatBox.chatForm[0]; // eslint-disable-next-line prefer-destructuring const chatInput = TankTrouble.ChatBox.chatInput[0]; // Create a mutation observer that looks for changes in the chatBody's attributes (namely width) new MutationObserver(() => { const width = Number(chatBody.offsetWidth || 220); chatForm.style.width = `${width}px`; chatInput.style.width = `${width - 12}px`; }).observe(chatBody, { attributes: true, characterData: false }); chatForm.style.width = '220px'; chatInput.style.width = `${chatForm.offsetWidth - 12}px`; // Allow more characters in the chat input chatInput.setAttribute('maxlength', '255'); });