您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
infinite scroll, filter: public, private, duration, positive/negative tags
当前为
// ==UserScript== // @name thisvid infinite scroll and filter // @license MIT // @namespace http://tampermonkey.net/ // @version 2.1 // @description infinite scroll, filter: public, private, duration, positive/negative tags // @author smartacephale // @match https://thisvid.com/ // @match https://thisvid.com/latest-updates/* // @match https://thisvid.com/tags/* // @match https://thisvid.com/categories/* // @match https://thisvid.com/*/?q=* // @match https://thisvid.com/members/* // @match https://thisvid.com/videos/* // @icon https://i.imgur.com/LAhknzo.jpeg // @grant GM_addStyle // @run-at document-end // ==/UserScript== const logo = ` ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░║▓▓▓▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░▒▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒░░░░░░░░░░╫█▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░▒▒▒▒▒░▒░░░░░░░░░░▒▒▒▒▒▒@@▒▒▒▒╖░▓▓▓▓▓▒░░░░░░░░░░░░░▒▒▒▒▒░░░░░░░░░░░░░ ░░░░░░░░░▒▒▒▒▒▒░▒░░░░░░░░░░░░░░░▒▒▒▒╣╣╢╢╣╣╣▓▓▓▓▓▓▓▓▓▓▓╣╣▒▒▒▒▒░░░░░░░░▒▒░░░░░░░░░ ░░░░░░░░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░▒▒▒▒╢╢╫▓▓▓▓▓▓▓▓▓▓▓▓▓╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒╢▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░▒░░░▒▒▒▒╫▓▓▓▓▓▓▓▓▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒╫▓▓▓▓▓▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒╫▓▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒╢▓▓▓▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒╢╣▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒╣╣╣▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒╣╣▒▒▒╢▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒░▒▒▒▒▒▒▒╢╢╣▒▒╢╣▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒╢╣╣▒▒╢▓▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒╢╫╣╣▒▒╢▓▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒░▒▒╣╢╣▒▒▒▒▓╣▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░▒▒▒▒▒▒╫╣▒▒▒▒▒╫╣▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒░▒▒╢╢▒▒╢╣╣╢╫╣╣▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒╢╫╣╣▒╢╣▒▒▒╣▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒╢▒▒▒╢▓▓╣▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▓▓╣▓╣▒▒▒▒░▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░▒░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒░▒▒▒╢▓▓▓╬▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒╢▓╣▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒╣╣▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒╣╣╣▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒╣╢╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒▒▒╢▒╣▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒╢▓▓╣╫▓╣▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░▒▒▒╫▓▓▓▓▓▓╣▒▒░░░░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░ ▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░▒▒▒▒╫▓▓▓▓▓▓▓▓▓╣╣▒▒░░░░░░░░░░░░░░░░░░░░▒▒░░░░░░░, ▒▒▒▒▒▒▒▒╣▒╣▒▒▒░▒░░░░░░░░░░░░░░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓╣╫╣▒▒░░░░░░░░░░░░░░▒▒░░░░░░░░░░ ▒▒▒▒▒▒▒▒╢╣╢╢▓▓╣▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓╣▒▒▒▒▒▒▒░░░▒▒▒▒▒░░░░░░░░░░░ ░░░░░░░░░░░░▒▒▒╨╨╢▒▒▒▒▒▒▒▒▒▒╨╨╜╜▒▒▒▒▒▒╜▒▒╨╨╨╨╨╨╨╨╨╨╨▀▒╫▒▒╜╣╫╝,░▒▒░]▒▒▒▒╨▒▒░╜Ñ▒╓─`; (function () { // biome-ignore lint/suspicious/noRedundantUseStrict: <explanation> 'use strict'; console.clear(); console.log(logo); // biome-ignore lint/complexity/noStaticOnlyClass: <explanation> class Utils { static $ = (x, e = document.body) => e.querySelector(x); static $$ = (x, e = document.body) => e.querySelectorAll(x); static findElementsBetweenIds = (startId, endId) => { const startElement = document.getElementById(startId); const endElement = document.getElementById(endId); if (!startElement || !endElement) { return []; } const elementsBetween = []; let currentElement = startElement.nextElementSibling; while (currentElement && currentElement !== endElement) { elementsBetween.push(currentElement); currentElement = currentElement.nextElementSibling; } return elementsBetween; }; static parseHTML = (s) => { const t = document.createElement('html'); t.innerHTML = s; return t; }; static fetchHtml = (url) => fetch(url).then((r) => r.text()).then((h) => parseHTML(h)); static parseCSSUrl = (s) => s.match(/(?<=\(").*(?="\))/)[0]; static timeToSeconds = (t) => (t.match(/\d+/gm) || ['0']) .reverse() .map((s, i) => parseInt(s) * 60 ** i) .reduce((a, b) => a + b) || 0; static circularShift = (n, c = 6, s = 1) => (n + s) % c || c; static isElementInViewport = (el) => { const rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }; static toggleClass = (element, className, condition) => { if (condition) { element.classList.add(className); } else { element.classList.remove(className); } }; static parseDOM = (html) => new DOMParser().parseFromString(html, 'text/html').body.firstChild; static parseIntegerOr = (n, or) => Number.isInteger(parseInt(n)) ? parseInt(n) : or; } const { $, $$, findElementsBetweenIds, fetchHtml, parseHTML, parseDOM, parseCSSUrl, circularShift, isElementInViewport, timeToSeconds, toggleClass, parseIntegerOr, } = Utils; class Tick { constructor(interval) { this.interval = interval; } start(callback) { if (this.ticker) { this.stop(); } callback(); this.ticker = setInterval(callback, this.interval); } stop() { clearInterval(this.ticker); this.ticker = false; } } class ReactiveLocalStorage { constructor(data) { if (data) { Object.assign(this, data); this.observeProps(this); } } getLS(prop) { return JSON.parse(localStorage.getItem(prop)); } setLS(prop, value) { localStorage.setItem(prop, JSON.stringify(value)); } toObservable(obj, prop) { const lsvalue = this.getLS(prop); let value = lsvalue !== null ? lsvalue : obj[prop]; Object.defineProperty(obj, prop, { get() { return value; }, set(newValue) { this.setLS(prop, newValue); value = newValue; }, }); } observeProps(obj) { for (const [key, _] of Object.entries(obj)) { this.toObservable(obj, key); } } } const SCROLL_RESET_DELAY = 500; const ANIMATION_DELAY = 750; class DomManager { constructor() { this.data = new Map(); this.container = this.createThumbsContainer(); } filterPositiveTags = () => { for (const [k, v] of this.data.entries()) { const containTagsNot = state.filterPositiveTags.some(tag => !k.includes(tag)); toggleClass(v.element, 'filtered-tag-pos', state.filterPositive && containTagsNot); }; } filterNegativeTags = () => { for (const [k, v] of this.data.entries()) { const containTags = state.filterNegativeTags.some(tag => k.includes(tag)); toggleClass(v.element, 'filtered-tag-neg', state.filterNegative && containTags); }; } thumbIsPrivate(t) { return t.firstElementChild.classList.contains('private'); } filterPrivate = (filterPrivate = state.filterPrivate) => { for (const v of this.data.values()) { toggleClass(v.element, 'filtered-private', filterPrivate && this.thumbIsPrivate(v.element)) }; } filterPublic = (filterPublic = state.filterPublic) => { for (const v of this.data.values()) { toggleClass(v.element, 'filtered-public', filterPublic && !this.thumbIsPrivate(v.element)) }; } filterByDuration = () => { const { filterDurationFrom: from, filterDurationTo: to, filterDuration } = state; for (const v of this.data.values()) { toggleClass(v.element, 'filtered-duration', filterDuration && (v.duration < from || v.duration > to)) }; } runFilters(container) { if (state.filterPrivate) this.filterPrivate(); if (state.filterPublic) this.filterPublic(); if (state.filterDuration) this.filterByDuration(); if (state.filterPositive) this.filterPositiveTags(); if (state.filterNegative) this.filterNegativeTags(); } createThumbsContainer() { return parseDOM('<div class="thumbs-items"></div>'); } handleLoadedHTML = (htmlPage, mount, useGlobalContainer = true) => { const thumbs = $$('.tumbpu', htmlPage); const container = !useGlobalContainer ? this.createThumbsContainer() : this.container; for (const thumbElement of thumbs) { const url = thumbElement.getAttribute('href'); if (!url || this.data.has(url)) { thumbElement.remove(); } else { this.data.set(url, { element: thumbElement, duration: timeToSeconds($('.thumb > .duration', thumbElement).textContent) }); const img = $('img', thumbElement); const privateEl = $('.private', thumbElement); if (privateEl) { img.src = parseCSSUrl(privateEl.style.background); privateEl.style.background = '#000'; } else { img.src = img.getAttribute('data-original'); } img.classList.add('tracking'); container.appendChild(thumbElement); } } this.runFilters(container); mount.before(container); }; } class PreviewManager { constructor() { this.tick = new Tick(ANIMATION_DELAY); } iteratePreviewImages(src) { return src.replace(/(\d)\.jpg$/, (_, n) => `${circularShift(parseInt(n))}.jpg`); } animatePreview = (e) => { const { target: el, type } = e; if (el.tagName === 'IMG' && el.classList.contains('tracking')) { if (type === 'mouseout') { this.tick.stop(); if (el.getAttribute('orig')) el.src = el.getAttribute('orig'); } if (type === 'mouseover') { if (!el.getAttribute('orig')) el.setAttribute('orig', el.src); this.tick.start(() => { el.src = this.iteratePreviewImages(el.src); }); } } }; listen(e) { e.addEventListener('mouseout', this.animatePreview); e.addEventListener('mouseover', this.animatePreview); } } class PaginationPageManager { constructor() { this.pagination = $('.pagination'); this.pagination.style.opacity = 0; handleLoadedHTML(document.body, this.pagination); previewManager.listen(this.pagination.parentElement); this.offsetLast = this.getOffsetLast(); this.paginationGenerator = this.createNextPageGenerator(); this.generatorDone = false; this.resetScroller(); this.tick = new Tick(SCROLL_RESET_DELAY); this.fixScrollViewPort(); this.ui = new UI(true); this.setPagIndex = (offset) => this.ui.setPagIndex(offset, this.offsetLast); this.setPagIndex(this.getCurrentOffset()); } fixScrollViewPort() { this.tick.start(() => { if (this.generatorDone) this.tick.stop(); if (isElementInViewport(this.pagination)) this.generatorConsumer(); }); } async generatorConsumer() { const { value: { url, offset } = {}, done, } = this.paginationGenerator.next(); this.generatorDone = done; if (!done) { const nextPageHTML = await fetchHtml(url); const prevScrollPos = document.documentElement.scrollTop; handleLoadedHTML(nextPageHTML, this.pagination); this.setPagIndex(offset); window.scrollTo(0, prevScrollPos); } } getCurrentOffset() { return parseInt(window.location.pathname.split(/(\d+\/)$/)[1] || '1'); } getOffsetLast() { return parseInt( $('.pagination-next').previousElementSibling.firstElementChild .textContent, ); } createNextPageGenerator() { let { origin, pathname, search } = window.location; let offset; [pathname, offset = '1'] = pathname.split(/(\d+\/)$/); offset = parseInt(offset); pathname = pathname === '/' ? '/latest-updates/' : pathname; const offsetLast = this.getOffsetLast(); function* nextPageGenerator() { for (let c = offset + 1; c <= offsetLast; c++) { const url = `${origin}${pathname}${c}/${search}`; console.log(url); yield { url, offset: c }; } } return nextPageGenerator(); } resetScroller = () => { this.infiniteScrollTriggered = false; window.dispatchEvent(new CustomEvent('scroll')); }; infiniteScroll = () => { const inViewport = isElementInViewport(this.pagination); if (inViewport === this.infiniteScrollTriggered) return; this.infiniteScrollTriggered = inViewport; if (inViewport) this.generatorConsumer(); }; } class Router { constructor() { this.route(); } route() { const { pathname } = window.location; if ($('.pagination-next')) { this.handlePaginationPage(); } else if (/\/members\/\d+\/$/.test(pathname)) { this.handleMemberPage(); } else if (/\/tag\//.test(pathname) || /\/?q=.*/.test(pathname)) { this.handlePageWithVideosButNoPagination(); } else if (/\/videos\//.test(pathname)) { //this.handlePageWithVideosButNoPagination() } } handlePageWithVideosButNoPagination() { const vid = $('.tumbpu'); if (!vid) return; handleLoadedHTML(document.body, vid.parentElement); previewManager.listen(vid.parentElement); const ui = new UI(false); } handlePaginationPage() { this.paginationManager = new PaginationPageManager(); } handleMemberPage() { const privates = $('#list_videos_private_videos_items'); if (privates) { const mistakes = findElementsBetweenIds( 'list_videos_private_videos_items', 'list_videos_favourite_videos', ); for (const m of mistakes) privates.appendChild(m); handleLoadedHTML(privates, privates, false); } const favorites = $('#list_videos_favourite_videos'); if (favorites) { const mountTo = favorites.firstElementChild.nextElementSibling; handleLoadedHTML(favorites, mountTo, false); } if (privates || favorites) { previewManager.listen((privates || favorites).parentElement); const ui = new UI(false); } } } class UI { templateHTML = (haspag) => ` <div id="tapermonkey-app"> <div class="subbox"> <input type="checkbox" id="filterNegative" name="filterNegative" ${state.filterNegative ? 'checked' : ''}/> <label for="filterNegative">negative filter</label> <textarea id="filterNegativeText" placeholder="tag1 tag2 ...">${state.filterNegativeTags.join(',')}</textarea> </div> <div class="subbox"> <input type="checkbox" id="filterPositive" name="filterPositive" ${state.filterPositive ? 'checked' : ''}/> <label for="filterPositive">positive filter</label> <textarea id="filterPositiveText" placeholder="tag1 tag2 ...">${state.filterPositiveTags.join(',')}</textarea> </div> <div class="subbox"> <input type="checkbox" id="filterPrivate" name="filterPrivate" ${state.filterPrivate ? 'checked' : ''}/> <label for="filterPrivate">filter private</label> <input type="checkbox" id="filterPublic" name="filterPublic" ${state.filterPublic ? 'checked' : ''}/> <label for="filterPublic">filter public</label> ${haspag ? '<span id="pagIndex">0/0</span>' : ''} </div> <div class="subbox"> <input type="checkbox" id="filterl" name="filterl" ${state.filterDuration ? 'checked' : ''}/> <label for="filterl">filter duration seconds</label> <label for="from">from</label> <input type="number" placeholder="min sec" step="10" min="0" max="72000" id="minL" name="from" value=${state.filterDurationFrom} /> <label for="to">to</label> <input type="number" placeholder="max sec" step="10" min="0" max="72000" id="maxL" name="to" value=${state.filterDurationTo} /> </div> </div>`; constructor(haspag = true) { document.body.appendChild(parseDOM(this.templateHTML(haspag))); this.tapermonkeyAppTemplate = document.querySelector('#tapermonkey-app'); this.control(); } setPagIndex(index, total) { $('#pagIndex').innerText = `${index}/${total}`; } stringToTags(s) { return s.split(",").map(s => s.trim()).filter(s => s.length > 0); } control() { this.tapermonkeyAppTemplate.addEventListener('input', (e) => { const { id, checked, value } = e.target; if (id === 'filterNegativeText') { state.filterNegativeTags = this.stringToTags(value); filterNegativeTags() } if (id === 'filterPositiveText') { state.filterPositiveTags = this.stringToTags(value); filterPositiveTags(); } }); this.tapermonkeyAppTemplate.addEventListener('click', (e) => { const { id, checked, value } = e.target; if (id === 'filterPublic') { state.filterPublic = checked; filterPublic(); } if (id === 'filterPrivate') { state.filterPrivate = checked; filterPrivate(); } if (id === 'filterNegative') { state.filterNegative = checked; filterNegativeTags() } if (id === 'filterPositive') { state.filterPositive = checked; filterPositiveTags(); } if (id === 'filterl') { state.filterDuration = checked; filterByDuration(); } if (id === 'minL') { state.filterDurationFrom = parseIntegerOr(value, state.filterDurationFrom); filterByDuration(); } if (id === 'maxL') { state.filterDurationTo = parseIntegerOr(value, state.filterDurationTo); filterByDuration(); } }); } } const state = new ReactiveLocalStorage({ filterDurationFrom: 0, filterDurationTo: 600, filterDuration: false, filterPrivate: false, filterPublic: false, infiniteScrollTriggered: false, filterNegativeTags: [], filterNegative: false, filterPositiveTags: [], filterPositive: false }); const { filterPrivate, filterPublic, filterByDuration, filterPositiveTags, filterNegativeTags, handleLoadedHTML } = new DomManager(); const previewManager = new PreviewManager(); const router = new Router(); const tampermonkeyCSS = ` #tapermonkey-app { background: #151515; padding: 10px; position: fixed; z-index: 9999; bottom: 10px; right: 10px; border-radius: 15px; width: max-content; box-shadow: 20px 20px 60px #000000, -20px -20px 60px #000000; } #tapermonkey-app .subbox { background: #2c2c2c; border-radius: 5px; padding: 4px; margin: 6px; user-select: none; display: flex; } #tapermonkey-app .subbox input[type=number] { width: 5rem; background: #26282b; } #tapermonkey-app .subbox input[type=checkbox] { margin-left: 5px; } #tapermonkey-app .subbox label { margin: 0 10px 0 0; } #pagIndex { text-align: end; margin-right: 5px; flex: 1;} #tapermonkey-app textarea { flex: 1; height: 2rem; padding: 6px; } #tapermonkey-app .subbox * { padding-left: 8px; float: none; width: auto; font-family: monospace; font-size: 0.8rem; align-self: center; color: #969696; } .tracking { content-visibility: auto; } .filtered-private, .filtered-duration, .filtered-public, .filtered-tag-pos, .filtered-tag-neg { display: none !important; } `; GM_addStyle(tampermonkeyCSS); })();