您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows user to move widgets like the chatbox to the left side of the app.
// ==UserScript== // @name Add button to move widget bar to left side - tradingview.com // @namespace Itsnotlupus Industries // @match https://*.tradingview.com/* // @grant none // @version 1.2 // @author itsnotlupus // @license MIT // @description Allows user to move widgets like the chatbox to the left side of the app. // @require https://gf.qytechs.cn/scripts/468394-itsnotlupus-tiny-utilities/code/utils.js // @require https://gf.qytechs.cn/scripts/471000-itsnotlupus-i18n-support/code/i18n.js // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // ==/UserScript== /* jshint esversion:11 */ const LEFT_TOOLBAR_WIDTH = 52; const RIGHT_WIDGETBAR_WIDTH = 46; (async function main() { const strings = { scriptFailure: 'UserScript "Add button to move widget bar to left side" has become incompatible with TradingView and can no longer work.\nPlease disable or update this script.', buttonLabel: "Move Widgets to Other Side of Chart", nagLog: "We found an UNCLOSABLE modal dialog. My favorite!" }; i18n.setEnStrings([], strings); await i18n.initLanguage(); // If we can't initialize within 60 seconds, let users know the script isn't able to work as intended. const failTimer = setTimeout(() => { const msg = t`scriptFailure`; console.log(`%c${msg}`, 'font-weight:600;font-size:2em;color:red'); throw new Error(msg); }, 60 * 1000); // wait for app to load and store refs to relevant elements const [ layoutAreaCenter, layoutAreaTradingPanel, layoutAreaLeft, layoutAreaRight, layoutAreaBottom, chartContainer, widgetbarPages, widgetbarWrap, widgetbarTabs, drawingToolbar, widgetToolbarFiller ] = await untilDOM(()=>{ const elts = [ $`.layout__area--center`, $`.layout__area--tradingpanel`, $`.layout__area--left`, $`.layout__area--right`, $`.layout__area--bottom`, $`.chart-container`, $`.widgetbar-pages`, $`.widgetbar-wrap`, $`.widgetbar-tabs`, $`#drawing-toolbar`, $`.widgetbar-tabs div[class^='filler-']` ]; return elts.some(e=>!e) ? null : elts; }); // If we're here, we're probably fine. clearTimeout(failTimer); // persistent state let widgetsMovedLeft = localStorage.widgetsMovedLeft === 'true'; let lastAd = 0; // augment UI // clone a button and customize it to make our "move widgets" button. const button = widgetToolbarFiller.nextElementSibling.cloneNode(true); button.dataset.name = "move_widgets"; button.dataset.tooltip = button.ariaLabel = t`buttonLabel`; button.querySelector('svg').remove(); button.querySelector('span').append(svg('svg', { width: 44, height: 44, viewBox: "0 0 21 21" }, // random SVG icon goes here. a UX person I am not. https://www.svgrepo.com/svg/343314/toggles svg('g', { fill:"none", "fill-rule":"evenodd", stroke:"currentColor", "stroke-width":"0.4", "stroke-linecap":"round", "stroke-linejoin":"round", transform:"translate(3 4)" }, svg('circle', { cx:"3.5", cy:"3.5", r:"3"}), svg('path', {d:"M6 1.5h6.5c.8 0 2 .3 2 2s-1.2 2-2 2H6m5.5 8a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"}), svg('path', {d:"M9 8.5H2.5c-.8 0-2 .3-2 2s1.2 2 2 2H9"}) ) )); button.addEventListener('click', () => toggleWidgets()); widgetToolbarFiller.after(button); // apply state to app. toggleWidgets(widgetsMovedLeft); // start observing DOM to adjust layout continuously. observeDOM(adjustLayout); function toggleWidgets(left = !widgetsMovedLeft) { const parent = left ? layoutAreaLeft : layoutAreaRight; parent.prepend(widgetbarWrap); widgetbarTabs.style.right = left ? '' : '0'; widgetbarTabs.style.left = left ? '0' : ''; widgetbarPages.style.right = left ? '51px' : ''; widgetsMovedLeft = left; localStorage.widgetsMovedLeft = left; adjustLayout(); } function adjustLayout() { const widgetWidth = RIGHT_WIDGETBAR_WIDTH + widgetbarPages.clientWidth; const rightWidth = widgetsMovedLeft ? 0 : widgetWidth; const leftWidth = LEFT_TOOLBAR_WIDTH + (widgetsMovedLeft ? widgetWidth : 0); const centerWidth = innerWidth - leftWidth - rightWidth - 8; const centerLeft = leftWidth + 4; const set = (elt, props) => Object.keys(props).forEach(p => elt.style[p] = props[p] + 'px'); set(drawingToolbar, { width: LEFT_TOOLBAR_WIDTH, marginLeft: leftWidth - LEFT_TOOLBAR_WIDTH + 2 }); set(layoutAreaRight, { width: rightWidth }); set(layoutAreaLeft, { width: leftWidth }); set(layoutAreaCenter, { width: centerWidth, left: centerLeft }); set(chartContainer, { width: centerWidth }); set(layoutAreaBottom, { width: centerWidth, left: centerLeft }); set(layoutAreaTradingPanel, { right: rightWidth }); // remove some nags since we're already here. $`div[data-role='toast-container']`.querySelector('button')?.click(); const gopro = $`[data-dialog-name='gopro']`; if (gopro) { // does it have a close button? const closeButton = gopro.querySelector('button[aria-label="close"],button[class^="close"]'); if (closeButton) { closeButton.click(); } else { // unclosable nag? Cool. console.error(t`nagLog`); gopro.parentElement.remove(); } } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址